Pythonic approach to zipping items in a list
Question:
Say I want to iterate through a list. Each time I iterate I want to compute something using both the current and next terms. I could do something like
mylist = [1, 2, 3, 4]
for i in range(len(mylist)):
try:
compute(mylist[i], mylist[i+1])
except IndexError:
compute(mylist[i])
I could also do
mylist = [1, 2, 3, 4]
for num in mylist:
try:
compute(num, mylist[mylist.index(num)+1])
except IndexError:
compute(num)
Neither of these seems particularly good. Is there a more pythonic approach to doing this?
Answers:
A functional style that doesn’t use itertools might look something like:
for t in zip(mylist, mylist[1:]):
compute(*t)
If performance is tantamount you can prevent the creation of the intermeida list during zip by using itertools.izip.
To cover the special case as shown in the question, use:
for t in zip(mylist, mylist[1:]) + [(mylist[-1],)]
compute(*t)
You can use built-in function enumerate
:
mylist = [1, 2, 3, 4]
for i, num in enumerate(mylist):
try:
compute(num, mylist[i+1])
except IndexError:
compute(num)
But choosing between your two implementations is rather easy – second is not only far slower (O(n^2)
), but also has weird semantics for repeating elements.
To cover your special case at the end, you need:
for t in zip(mylist, mylist[1:]) + [(mylist[-1],)]
compute(*t)
There are three ways to do that (although I would probably prefer first one, unless your conditions are different):
-
Use itertools.izip()
– it will be efficient and Pythonic:
for item1, item2 in itertools.izip(my_list, my_list[1:]):
# do something...
-
Use enumerate()
:
for index, item in enumerate(my_list):
# do something...
-
Store previous row in a variable (the example assumes there is no None
in your list):
previous = None
for item in my_list:
if previous is None:
previous = item
continue
# do something...
Say I want to iterate through a list. Each time I iterate I want to compute something using both the current and next terms. I could do something like
mylist = [1, 2, 3, 4]
for i in range(len(mylist)):
try:
compute(mylist[i], mylist[i+1])
except IndexError:
compute(mylist[i])
I could also do
mylist = [1, 2, 3, 4]
for num in mylist:
try:
compute(num, mylist[mylist.index(num)+1])
except IndexError:
compute(num)
Neither of these seems particularly good. Is there a more pythonic approach to doing this?
A functional style that doesn’t use itertools might look something like:
for t in zip(mylist, mylist[1:]):
compute(*t)
If performance is tantamount you can prevent the creation of the intermeida list during zip by using itertools.izip.
To cover the special case as shown in the question, use:
for t in zip(mylist, mylist[1:]) + [(mylist[-1],)]
compute(*t)
You can use built-in function enumerate
:
mylist = [1, 2, 3, 4]
for i, num in enumerate(mylist):
try:
compute(num, mylist[i+1])
except IndexError:
compute(num)
But choosing between your two implementations is rather easy – second is not only far slower (O(n^2)
), but also has weird semantics for repeating elements.
To cover your special case at the end, you need:
for t in zip(mylist, mylist[1:]) + [(mylist[-1],)]
compute(*t)
There are three ways to do that (although I would probably prefer first one, unless your conditions are different):
-
Use
itertools.izip()
– it will be efficient and Pythonic:for item1, item2 in itertools.izip(my_list, my_list[1:]): # do something...
-
Use
enumerate()
:for index, item in enumerate(my_list): # do something...
-
Store previous row in a variable (the example assumes there is no
None
in your list):previous = None for item in my_list: if previous is None: previous = item continue # do something...