Related
This question already has answers here:
Edit the value of every Nth item in a list
(7 answers)
how to replace every n-th value of an array in python most efficiently?
(3 answers)
Closed 3 years ago.
How do I check every third element in a list?
ie. Given the list below, I want to mutate every third item and leave the rest of the list as is.
L = [1,2,3,4,5,6,7,8,9]
Basically, if the third element in the list (in this case 3,6,9) is odd, then subtract one to make it even. If it is even, let it remain the same.
I know that if I do L[2::3] I get a new list of every third element, but I simply want to loop through every third item and check.
What would the for loop look like? I need some help.
Thanks and appreciate all help!
Don't overthink this. This is a case where list comprehensions, functions, and so on are all overkill. You need to iterate over a list, and modify it in-place, right? So you're not modifying the structure you're iterating over. The iteration target is actually the indices of the list.
xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(xs)
for idx in range(2, len(xs), 3):
if xs[idx] % 2 == 1:
xs[idx] -= 1
print(xs)
This is the equivalent of a for(i = 2; i < len(xs); i++) loop in many lower-level languages.
Compared to the list comprehension solution proposed in another answer, the for loop is marginally faster:
from timeit import timeit
def in_place(xs):
for idx in range(2, len(xs), 3):
if xs[idx] % 2 == 1:
xs[idx] -= 1
return xs
def list_comp(xs):
xs[2::3] = [i - 1 if (i % 3 == 0 and i % 2 != 0) else i for i in xs[2::3]]
return xs
# This answer is an improvement, as it eliminates one modulus
def list_comp2(xs):
xs[2::3] = [x - 1 if x % 2 else x for x in xs[2::3]]
return xs
context = {"globals": globals(), "setup": "xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]"}
print(f"For loop: {timeit('in_place(xs)', **context)}")
print(f"List comprehension: {timeit('list_comp(xs)', **context)}")
print(f"List comprehension 2: {timeit('list_comp2(xs)', **context)}")
On my machine, this produces:
For loop: 0.608657514
List comprehension: 0.7510721879999999
List comprehension 2: 0.641639047
You can just do a list comprehension using slicing to modify selected values based on condition:
L[2::3] = [x - 1 if x % 2 else x for x in L[2::3]]
Example:
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [x - 1 if x % 2 else x for x in L[2::3]]
# [1, 2, 2, 4, 5, 6, 7, 8, 8]
You can assign to slices and in this example then use a list comprehension to do the mutation.
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [ x if x % 2 == 0 else x-1 for x in L[2::3] ]
print(L)
Though despite the syntactic beauty, a simple for loop reads better for your future self and colleagues =D
Define the function you want to apply to the elements an iterate using a list comprehension
def do(number):
if number%2 == 1:
return number -1
else:
return number
[do(y) if i%3 == 0 else y for i,y in enumerate(L)]
a different solution not using enumerate explicitly could be:
for i in range(len(L)):
if i%3 == 0:
L[i] = do(L[i])
You also might want to check this about inlace operations
List comprehension is the best option:
L = [1,2,3,4,5,6,7,8,9]
L[2::3] = [i-1 if (i%3==0 and i%2!= 0) else i for i in L[2::3]
print(L)
Output:
[1, 2, 2, 4, 5, 6, 7, 8, 8]
Explanation:
Basically you look at each third element in L and see if it is divisible by 3 (remainder is 0) AND not even (mod2 is not 0) and if so subtract 1 from it, otherwise keep it as is. Replace result in the original list.
In a homework set I'm working on, I've come across the following question, which I am having trouble answering in a Python-3 function:
"Write a function alternate : int list -> int that takes a list of
numbers and adds them with alternating sign. For example alternate
[1,2,3,4] = 1 - 2 + 3 - 4 = -2."
Full disclosure, the question was written with Standard ML in mind but I have been attempting to learn Python and came across the question. I'm imagining it involves some combination of:
splitting the list,
if [i] % 2 == 0:
and then concatenating the alternate plus and minus signs.
def alternate(l):
return sum(l[::2]) - sum(l[1::2])
Take the sum of all the even indexed elements and subtract the sum of all the odd indexed elements. Empty lists sum to 0 so it coincidently handles lists of length 0 or 1 without code specifically for those cases.
References:
list slice examples
sum()
Not using fancy modules or operators since you are learning Python.
>>> mylist = range(2,20,3)
>>> mylist
[2, 5, 8, 11, 14, 17]
>>> sum(item if i%2 else -1*item for i,item in enumerate(mylist, 1))
-9
>>>
How it works?
>>> mylist = range(2,20,3)
>>> mylist
[2, 5, 8, 11, 14, 17]
enumerate(mylist, 1) - returns each item in the list and its index in the list starting from 1
If the index is odd, then add the item. If the index is even add the negative of the item.
if i%2:
return item
else:
return -1*item
Add everything using sum bulitin.
>>> sum(item if i%2 else -1*item for i,item in enumerate(mylist, 1))
-9
>>>
Although this already has an accepted answer I felt it would be better to also provide a solution that isn't a one-liner.
def alt_sum(lst):
total = 0
for i, value in enumerate(lst):
# checks if current index is odd or even
# if even then add, if odd then subtract
if i % 2 == 0:
total += value
else:
total -= value
return total
>>> alt_sum([1, 2, 3, 4])
-2
my_list = range(3, 20, 2)
sum(item * ((-1)**index) for index, item in enumerate(my_list))
sum = 11 (result of 3-5+7-9+11-13+15-17+19)
You could try this list comprehension:
sum([-e if c%2 else e for c,e in enumerate(yourlistylist)])
Here is one way using operator module:
In [21]: from operator import pos, neg
In [23]: ops = (pos, neg)
In [24]: sum(ops[ind%2](value) for ind, value in enumerate(lst))
Out[24]: -2
You can use cycle from itertools to alternate +/-
>>> from itertools import cycle
>>> data = [*range(1, 5)]
>>> sum(i * s for i, s in zip(data, cycle([1, -1])))
-2
I have a list numbers say,
[1,2,3,6,8,9,10,11]
First, I want to get the sum of the differences (step size) between the numbers (n, n+1) in the list.
Second, if a set of consecutive numbers having a difference of 1 between them, put them in a list, i.e. there are two such lists in this example,
[1,2,3]
[8,9,10,11]
and then put the rest numbers in another list, i.e. there is only one such list in the example,
[6].
Third, get the lists with the max/min sizes from the sequential lists, i.e. [1,2,3], [8,9,10,11] in this example, the max list is,
[8,9,10,11]
min list is
[1,2,3].
What's the best way to implement this?
First, I want to get the sum of the differences (step size) between
the numbers (n, n+1) in the list.
Use sum on the successive differences of elements in the list:
>>> sum(lst[i] - x for i, x in enumerate(lst[:-1], start=1))
10
Second, if a set of consecutive numbers having a difference of 1 between them, put them in a list, i.e. there are two such lists in
this example, and then put the rest numbers in another list, i.e.
there is only one such list in the example,
itertools.groupby does this by grouping on the difference of each element on a reference itertools.count object:
>>> from itertools import groupby, count
>>> c = count()
>>> result = [list(g) for i, g in groupby(lst, key=lambda x: x-next(c))]
>>> result
[[1, 2, 3, 4], [6], [8, 9, 10, 11]]
Third, get the lists with the max/min sizes from above
max and min with the key function as sum:
>>> max(result, key=sum)
[8, 9, 10, 11]
>>> min(result, key=sum)
[6] #??? shouldn't this be [6]
I wonder if you've already got the answer to this (given the missing 4 from your answers) as the first thing I naively tried produced that answer. (That and/or it reads like a homework question)
>>> a=[1,2,3,4,6,8,9,10,11]
>>> sum([a[x+1] - a[x] for x in range(len(a)-1)])
10
>>> [a[x] for x in range(len(a)-1) if abs(a[x] - a[x+1]) ==1]
[1, 2, 3, 8, 9, 10]
Alternatively, try :
a=[1,2,3,6,8,9,10,11]
sets = []
cur_set = set()
total_diff = 0
for index in range(len(a)-1):
total_diff += a[index +1] - a[index]
if a[index +1] - a[index] == 1:
cur_set = cur_set | set([ a[index +1], a[index]])
else:
if len(cur_set) > 0:
sets.append(cur_set)
cur_set = set()
if len(cur_set) > 0:
sets.append(cur_set)
all_seq_nos = set()
for seq_set in sets:
all_seq_nos = all_seq_nos | seq_set
non_seq_set = set(a) - all_seq_nos
print("Sum of differences is {0:d}".format(total_diff))
print("sets of sequential numbers are :")
for seq_set in sets:
print(sorted(list(seq_set)))
print("set of non-sequential numbers is :")
print(sorted(list(non_seq_set)))
big_set=max(sets, key=sum)
sml_set=min(sets, key=sum)
print ("Biggest set of sequential numbers is :")
print (sorted(list(big_set)))
print ("Smallest set of sequential numbers is :")
print (sorted(list(sml_set)))
Which will produce the output :
Sum of differences is 10
sets of sequential numbers are :
[1, 2, 3]
[8, 9, 10, 11]
set of non-sequential numbers is :
[6]
Biggest set of sequential numbers is :
[8, 9, 10, 11]
Smallest set of sequential numbers is :
[1, 2, 3]
Hopefully that all helps ;-)
Given a list
a = [0,1,2,3,4,5,6,7,8,9]
how can I get
b = [0,9,1,8,2,7,3,6,4,5]
That is, produce a new list in which each successive element is alternately taken from the two sides of the original list?
>>> [a[-i//2] if i % 2 else a[i//2] for i in range(len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
Explanation:
This code picks numbers from the beginning (a[i//2]) and from the end (a[-i//2]) of a, alternatingly (if i%2 else). A total of len(a) numbers are picked, so this produces no ill effects even if len(a) is odd.
[-i//2 for i in range(len(a))] yields 0, -1, -1, -2, -2, -3, -3, -4, -4, -5,
[ i//2 for i in range(len(a))] yields 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
and i%2 alternates between False and True,
so the indices we extract from a are: 0, -1, 1, -2, 2, -3, 3, -4, 4, -5.
My assessment of pythonicness:
The nice thing about this one-liner is that it's short and shows symmetry (+i//2 and -i//2).
The bad thing, though, is that this symmetry is deceptive:
One might think that -i//2 were the same as i//2 with the sign flipped. But in Python, integer division returns the floor of the result instead of truncating towards zero. So -1//2 == -1.
Also, I find accessing list elements by index less pythonic than iteration.
cycle between getting items from the forward iter and the reversed one. Just make sure you stop at len(a) with islice.
from itertools import islice, cycle
iters = cycle((iter(a), reversed(a)))
b = [next(it) for it in islice(iters, len(a))]
>>> b
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
This can easily be put into a single line but then it becomes much more difficult to read:
[next(it) for it in islice(cycle((iter(a),reversed(a))),len(a))]
Putting it in one line would also prevent you from using the other half of the iterators if you wanted to:
>>> iters = cycle((iter(a), reversed(a)))
>>> [next(it) for it in islice(iters, len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
>>> [next(it) for it in islice(iters, len(a))]
[5, 4, 6, 3, 7, 2, 8, 1, 9, 0]
A very nice one-liner in Python 2.7:
results = list(sum(zip(a, reversed(a))[:len(a)/2], ()))
>>>> [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
First you zip the list with its reverse, take half that list, sum the tuples to form one tuple, and then convert to list.
In Python 3, zip returns a generator, so you have have to use islice from itertools:
from itertools import islice
results = list(sum(islice(zip(a, reversed(a)),0,int(len(a)/2)),()))
Edit: It appears this only works perfectly for even-list lengths - odd-list lengths will omit the middle element :( A small correction for int(len(a)/2) to int(len(a)/2) + 1 will give you a duplicate middle value, so be warned.
Use the right toolz.
from toolz import interleave, take
b = list(take(len(a), interleave((a, reversed(a)))))
First, I tried something similar to Raymond Hettinger's solution with itertools (Python 3).
from itertools import chain, islice
interleaved = chain.from_iterable(zip(a, reversed(a)))
b = list(islice(interleaved, len(a)))
If you don’t mind sacrificing the source list, a, you can just pop back and forth:
b = [a.pop(-1 if i % 2 else 0) for i in range(len(a))]
Edit:
b = [a.pop(-bool(i % 2)) for i in range(len(a))]
Not terribly different from some of the other answers, but it avoids a conditional expression for determining the sign of the index.
a = range(10)
b = [a[i // (2*(-1)**(i&1))] for i in a]
i & 1 alternates between 0 and 1. This causes the exponent to alternate between 1 and -1. This causes the index divisor to alternate between 2 and -2, which causes the index to alternate from end to end as i increases. The sequence is a[0], a[-1], a[1], a[-2], a[2], a[-3], etc.
(I iterate i over a since in this case each value of a is equal to its index. In general, iterate over range(len(a)).)
The basic principle behind your question is a so-called roundrobin algorithm. The itertools-documentation-page contains a possible implementation of it:
from itertools import cycle, islice
def roundrobin(*iterables):
"""This function is taken from the python documentation!
roundrobin('ABC', 'D', 'EF') --> A D E B F C
Recipe credited to George Sakkis"""
pending = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables) # next instead of __next__ for py2
while pending:
try:
for next in nexts:
yield next()
except StopIteration:
pending -= 1
nexts = cycle(islice(nexts, pending))
so all you have to do is split your list into two sublists one starting from the left end and one from the right end:
import math
mid = math.ceil(len(a)/2) # Just so that the next line doesn't need to calculate it twice
list(roundrobin(a[:mid], a[:mid-1:-1]))
# Gives you the desired result: [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
alternatively you could create a longer list (containing alternating items from sequence going from left to right and the items of the complete sequence going right to left) and only take the relevant elements:
list(roundrobin(a, reversed(a)))[:len(a)]
or using it as explicit generator with next:
rr = roundrobin(a, reversed(a))
[next(rr) for _ in range(len(a))]
or the speedy variant suggested by #Tadhg McDonald-Jensen (thank you!):
list(islice(roundrobin(a,reversed(a)),len(a)))
Not sure, whether this can be written more compactly, but it is efficient as it only uses iterators / generators
a = [0,1,2,3,4,5,6,7,8,9]
iter1 = iter(a)
iter2 = reversed(a)
b = [item for n, item in enumerate(
next(iter) for _ in a for iter in (iter1, iter2)
) if n < len(a)]
For fun, here is an itertools variant:
>>> a = [0,1,2,3,4,5,6,7,8,9]
>>> list(chain.from_iterable(izip(islice(a, len(a)//2), reversed(a))))
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
This works where len(a) is even. It would need a special code for odd-lengthened input.
Enjoy!
Not at all elegant, but it is a clumsy one-liner:
a = range(10)
[val for pair in zip(a[:len(a)//2],a[-1:(len(a)//2-1):-1]) for val in pair]
Note that it assumes you are doing this for a list of even length. If that breaks, then this breaks (it drops the middle term). Note that I got some of the idea from here.
Two versions not seen yet:
b = list(sum(zip(a, a[::-1]), ())[:len(a)])
and
import itertools as it
b = [a[j] for j in it.accumulate(i*(-1)**i for i in range(len(a)))]
mylist = [0,1,2,3,4,5,6,7,8,9]
result = []
for i in mylist:
result += [i, mylist.pop()]
Note:
Beware: Just like #Tadhg McDonald-Jensen has said (see the comment below)
it'll destroy half of original list object.
One way to do this for even-sized lists (inspired by this post):
a = range(10)
b = [val for pair in zip(a[:5], a[5:][::-1]) for val in pair]
I would do something like this
a = [0,1,2,3,4,5,6,7,8,9]
b = []
i = 0
j = len(a) - 1
mid = (i + j) / 2
while i <= j:
if i == mid and len(a) % 2 == 1:
b.append(a[i])
break
b.extend([a[i], a[j]])
i = i + 1
j = j - 1
print b
You can partition the list into two parts about the middle, reverse the second half and zip the two partitions, like so:
a = [0,1,2,3,4,5,6,7,8,9]
mid = len(a)//2
l = []
for x, y in zip(a[:mid], a[:mid-1:-1]):
l.append(x)
l.append(y)
# if the length is odd
if len(a) % 2 == 1:
l.append(a[mid])
print(l)
Output:
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
I want to write a small code in python that Swap Elements in a list this program will accept a list, and will return a list that exchanges the positions of each pair of adjacent elements: positions 0 and 1, positions 2 and 3, and so on. If the list has an odd number of elements, then the element in the last position stays “in place”.
Before: [1,2,3,4,5]
After: [2,1,4,3,5]
This looks unpythonic. What is the Python way to do it?
Here is a neat one, if you are always guaranteed to have even numbers:
nums = [1,2,3,4,5,6]
print([nums[i^1] for i in range(len(nums))])
>>[2, 1, 4, 3, 6, 5]
Explanation:
print (0^1) #1
print (1^1) #0
print (2^1) #3
print (3^1) #2
print (4^1) #5
print (5^1) #4
As a refresher, the XOR has the following effect:
A B | Output
---------------
0 0 0
0 1 1
1 0 1
1 1 0
And the official description: Each bit of the output is the same as the corresponding bit in x if that bit in y is 0, and it's the complement of the bit in x if that bit in y is 1.
Most pythonic way:
def swappairwise(a):
l = len(a)&~1
a[1:l:2],a[:l:2] = a[:l:2],a[1:l:2]
Building on the answer above from #Arpegius, here a, hopefully, somewhat more readable solution. Uses the same approach.
def swap_list_pairwise(lis):
"""Pairwise swap of all elements in a list.
If the number of elements is odd, the leftover element
stays at its place.
"""
length = len(lis)
# Stop at second last if length is odd, otherwise use full list.
end = length - 1 if length % 2 else length
lis[1:end:2], lis[:end:2] = lis[:end:2], lis[1:end:2]
If you want "pythonic", try "How do you split a list into evenly sized chunks in Python?", followed by a map() that reverses every chunk. May not be too performant, though.
(Oh, forgot the flattening of the list at the end)
Here is a way:
def pairwise_swap(iterable):
for i, value in enumerate(iterable):
if i % 2 == 0:
saved = value
else:
yield value
yield saved
>>> list(pairwise_swap(range(10)))
[1, 0, 3, 2, 5, 4, 7, 6, 9, 8]
Nice approach by #Alok above. This should fix the missing last
element if the number of elements is odd.
def pairwise_swap(iterable):
"""Pairwise swap of all elements in an iterable.
If the number of elements is odd, the leftover element
stays at its place.
"""
for i, value in enumerate(iterable):
if i % 2 == 0:
saved = value
else:
yield value
yield saved
# Don't forget the last element if `iterable` has an odd
# number of elements. Since counting starts a zero, we need
# to test if `i` is even.
if iterable and i % 2 == 0:
yield value
How about trying this one out?
>>> l = [1,2,3,4,5,6]
>>> pl = [l[i:i+2] for i in range(0,len(l),2)]
>>> pl
[[1, 2], [3, 4], [5, 6]]
>>> for i in pl:
... i[0],i[1] = i[1],i[0]
... print i
...
[2, 1]
[4, 3]
[6, 5]
>>> pl
[[2, 1], [4, 3], [6, 5]]
>>>
>>> zpl = [i for sublist in pl for i in sublist]
>>> zpl
[2, 1, 4, 3, 6, 5]
>>>
I tried to resolve in the easiest way out.
It will work for both even and odd elements. if list elements are even
first part will work if not else will do his task.
a = list(input("Put your list here: "))
len_a = len(a)
last_element = len_a-1
result = 0
if len_a % 2==0:
for i in range(0, len(a), 2):
a[i], a[i + 1] = a[i + 1], a[i]
print("its if",a)
else:
a_n = a.pop()
for i in range(0, len(a), 2):
a[i], a[i + 1] = a[i + 1], a[i]
a.insert(0,a_n)
# a.insert(last_element,a_n) if you want last element remain unmodified
print("its else:",a)
This is what worked for me. Hopefully, it can help others. I think about using boxes. The value in the first position needs a temporary place to be before it can be swapped, so I assign it to x and then make the swap. Incrementing by 2 will skip over the last element, so it's okay with odd-numbered lists as well.
a = [int(s) for s in input().split()]
i = 0
while i in range(0, len(a) - 1):
x = a[i]
a[i] = a[i + 1]
a[i + 1] = x
i += 2
print(a)
Found this to be much simpler solution and handels the list with odd elements
elements = list(map(int,input().split()))
swap = []
i = 0
while i < len(elements):
if i+1 < len(elements):
swap.append(elements[i+1])
swap.append(elements[i])
else:
swap.append(elements[-1])
i = i+2
print(swap)