# Modifying every alternate element of a sequence

Discussion in 'Python' started by jm.suresh@no.spam.gmail.com, Nov 28, 2006.

1. ### Guest

I have a list of numbers and I want to build another list with every
second element multiplied by -1.

input = [1,2,3,4,5,6]
wanted = [1,-2,3,-4,5,-6]

I can implement it like this:

input = range(3,12)
wanted = []
for (i,v) in enumerate(input):
if i%2 == 0:
wanted.append(v)
else:
wanted.append(-v)

But is there any other better way to do this.

--
Suresh

, Nov 28, 2006

2. ### Tim ChaseGuest

> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.
>
> input = [1,2,3,4,5,6]
> wanted = [1,-2,3,-4,5,-6]
>
> I can implement it like this:
>
> input = range(3,12)
> wanted = []
> for (i,v) in enumerate(input):
> if i%2 == 0:
> wanted.append(v)
> else:
> wanted.append(-v)

>>> input = range(3,12)
>>> [i%2==0 and v or -v for (i,v) in enumerate(input)]

[3, -4, 5, -6, 7, -8, 9, -10, 11]

> But is there any other better way to do this.

I'm not sure densely packing it into a list comprehension is
necessarily a *better* way, just a more compact way.

To make more sense of it, you might create a helper function that

def inv_if(v, test):
if test:
return v
else:
return -v

[inv_if(v, i%2==0) for (i,v) in enumerate(input)]

Or you could even do something like

def inv_alternating(t):
i, v = t
if i%2==0:
return v
else:
return -v

[inv_alternating(t) for t in enumerate(input)]

Either compacts it for the actual call within a list
comprehension, but it is cleaner to read what's going on.

-tkc

Tim Chase, Nov 28, 2006

3. ### John HickenGuest

wrote:
> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.
>
> input = [1,2,3,4,5,6]
> wanted = [1,-2,3,-4,5,-6]
>
> I can implement it like this:
>
> input = range(3,12)
> wanted = []
> for (i,v) in enumerate(input):
> if i%2 == 0:
> wanted.append(v)
> else:
> wanted.append(-v)
>
> But is there any other better way to do this.
>
> --
> Suresh

I would tend to do this as a list comprehension. In python 2.5 you can
do this:

wanted = [(v if i % 2 == 0 else -v) for (i,v) in enumerate(input)]

(a if b else c) is new to Python 2.5. You don't always need the
brackets, but I think it makes things clearer. See
(http://docs.python.org/whatsnew/pep-308.html) for more details on this
feature.

With earlier versions, you could do

wanted = [v - 2*(i % 2) for (i,v) in enumerate(input)]

That looks less clear to me than your version, though.

John Hicken

John Hicken, Nov 28, 2006
4. ### Leo KislovGuest

wrote:
> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.
>
> input = [1,2,3,4,5,6]
> wanted = [1,-2,3,-4,5,-6]
>
> I can implement it like this:
>
> input = range(3,12)
> wanted = []
> for (i,v) in enumerate(input):
> if i%2 == 0:
> wanted.append(v)
> else:
> wanted.append(-v)
>
> But is there any other better way to do this.

Use slices:

input[1::2] = [-item for item in input[1::2]]

If you don't want to do it in-place, just make a copy:

wanted = input[:]
wanted[1::2] = [-item for item in wanted[1::2]]

-- Leo

Leo Kislov, Nov 28, 2006
5. ### Antoon PardonGuest

On 2006-11-28, <> wrote:
> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.
>
> input = [1,2,3,4,5,6]
> wanted = [1,-2,3,-4,5,-6]
>
> I can implement it like this:
>
> input = range(3,12)
> wanted = []
> for (i,v) in enumerate(input):
> if i%2 == 0:
> wanted.append(v)
> else:
> wanted.append(-v)
>
> But is there any other better way to do this.

Wether this is better, I'll leave that for others to decide. But this
is a possibility:

wanted = [ (1 - 2*(i%2)) * item for i, item in enumerate(input)]

--
Antoon Pardon

Antoon Pardon, Nov 28, 2006
6. ### Guest

Wow, I was in fact searching for this syntax in the python tutorial. It
is missing there.
Is there a reference page which documents all possible list
comprehensions.
--
Suresh
Leo Kislov wrote:
> wrote:
> > I have a list of numbers and I want to build another list with every
> > second element multiplied by -1.
> >
> > input = [1,2,3,4,5,6]
> > wanted = [1,-2,3,-4,5,-6]
> >
> > I can implement it like this:
> >
> > input = range(3,12)
> > wanted = []
> > for (i,v) in enumerate(input):
> > if i%2 == 0:
> > wanted.append(v)
> > else:
> > wanted.append(-v)
> >
> > But is there any other better way to do this.

>
> Use slices:
>
> input[1::2] = [-item for item in input[1::2]]
>
> If you don't want to do it in-place, just make a copy:
>
> wanted = input[:]
> wanted[1::2] = [-item for item in wanted[1::2]]
>
> -- Leo

, Nov 28, 2006
7. ### Roberto BonvalletGuest

wrote:
> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.

[...]
> But is there any other better way to do this.

I think the best way is the one that uses slices, as somebody suggested
in this thread. This is another (worse) way, just for fun:

>>> from itertools import cycle
>>> input = [1, 2, 3, 4, 5, 6]
>>> wanted = [x * sign for x, sign in zip(input, cycle([1, -1]))]
>>> wanted

[1, -2, 3, -4, 5, -6]

Cheers,
--
Roberto Bonvallet

Roberto Bonvallet, Nov 28, 2006
8. ### Steven D'ApranoGuest

On Tue, 28 Nov 2006 02:38:09 -0800, wrote:

> I have a list of numbers and I want to build another list with every
> second element multiplied by -1.
>
> input = [1,2,3,4,5,6]
> wanted = [1,-2,3,-4,5,-6]
>
> I can implement it like this:
>
> input = range(3,12)
> wanted = []
> for (i,v) in enumerate(input):
> if i%2 == 0:
> wanted.append(v)
> else:
> wanted.append(-v)
>
> But is there any other better way to do this.

Lots of ways.

Other people have given you some solutions. In my opinion, this is
the simplest method of all, if you want to modify input in place:

for i in range(1, len(input), 2):
input[where] = -input[where]

Here's another method that only works if there are an even number of items:

A = input[0::2] # extended slicing
B = input[1::2]
B = [-x for x in B]
tmp = zip(A, B) # but watch out for odd number of items!
result = []
for t in tmp:
result.extend(t)

Here's a third method:

factors = [(-1)**i for i in range(len(input))]
output = map(operator.mul, input, factors)

As for which is best, I leave that to you to decide.

--
Steven.

Steven D'Aprano, Nov 28, 2006
9. ### Leo KislovGuest

wrote:
> Wow, I was in fact searching for this syntax in the python tutorial. It
> is missing there.
> Is there a reference page which documents all possible list
> comprehensions.

There is actually only two forms of list comprehensions:
http://docs.python.org/ref/lists.html
[blah for x in expr] and [blah for x in expr if cond]

And here is reference page for slicing (note, it's not list
comprehension): http://docs.python.org/ref/slicings.html

-- Leo

Leo Kislov, Nov 28, 2006
10. ### Guest

Leo Kislov:
> input[1::2] = [-item for item in input[1::2]]
> If you don't want to do it in-place, just make a copy:
> wanted = input[:]
> wanted[1::2] = [-item for item in wanted[1::2]]

Very nice solution.
I have tried few versions like:
from itertools import imap, islice
from operator import neg
1) data[1::2] = [-el for el in data[1::2]]
2) data[1::2] = map(neg, data[1::2])
3) data[1::2] = imap(neg, data[1::2])
4) data[1::2] = map(neg, islice(data, 1, None, 2))
5) etc.

With Python 2.5 it seems that the n.2 (map + slicing) is the faster.

Bye,
bearophile

, Nov 28, 2006