Velocity Reviews > Modifying every alternate element of a sequence

# Modifying every alternate element of a sequence

jm.suresh@no.spam.gmail.com
Guest
Posts: n/a

 11-28-2006
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

Tim Chase
Guest
Posts: n/a

 11-28-2006
> 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
does your comparison work:

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

John Hicken
Guest
Posts: n/a

 11-28-2006

http://www.velocityreviews.com/forums/(E-Mail Removed) 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

Leo Kislov
Guest
Posts: n/a

 11-28-2006

(E-Mail Removed) 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

Antoon Pardon
Guest
Posts: n/a

 11-28-2006
On 2006-11-28, (E-Mail Removed) <(E-Mail Removed)> 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

jm.suresh@no.spam.gmail.com
Guest
Posts: n/a

 11-28-2006
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:
> (E-Mail Removed) 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

Roberto Bonvallet
Guest
Posts: n/a

 11-28-2006
(E-Mail Removed) 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

Steven D'Aprano
Guest
Posts: n/a

 11-28-2006
On Tue, 28 Nov 2006 02:38:09 -0800, (E-Mail Removed) 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.

Leo Kislov
Guest
Posts: n/a

 11-28-2006
(E-Mail Removed) 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

bearophileHUGS@lycos.com
Guest
Posts: n/a

 11-28-2006
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