Reputation: 1282
I am wondering if there is a better way to iterate two items at a time in a list. I work with Maya a lot, and one of its commands (listConnections) returns a list of alternating values. The list will look like [connectionDestination, connectionSource, connectionDestination, connectionSource]. To do anything with this list, I would ideally like to do something similar to:
for destination, source in cmds.listConnections():
print source, destination
You could, of course just iterate every other item in the list using [::2] and enumerate and source would be the index+1, but then you have to add in extra checks for odd numbered lists and stuff.
The closest thing I have come up with so far is:
from itertools import izip
connections = cmds.listConnections()
for destination, source in izip(connections[::2], connections[1::2]):
print source, destination
This isn't super important, as I already have ways of doing what I want. This just seems like one of those things that there should be a better way of doing it.
Upvotes: 19
Views: 16848
Reputation: 208545
You can use the following method for grouping items from an iterable, taken from the documentation for zip()
:
connections = cmds.listConnections()
for destination, source in zip(*[iter(connections)]*2):
print source, destination
Or for a more readable version, use the grouper recipe from the itertools documentation:
def grouper(n, iterable, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
Upvotes: 18
Reputation: 91
lis = list(range(12))
lg = len(lis)
tup = tuple()
for k in range(0,lg,2):
tup = (lis[k],lis[k+1])
print(tup)
(0, 1) (2, 3) (4, 5) (6, 7) (8, 9) (10, 11)
Upvotes: 2
Reputation: 714
why not just use range
with the step=2
?
for i in range(0, len(connections)):
source, destination = connections[i:i + 2]
print source, destination
you can make a generic function out of it like:
def tuples(iterable, arity):
return [iterable[i:i+arity] for i in range(0, len(iterable), arity)]
Upvotes: 3
Reputation: 141
All, Great question & answer. I'd like to provide another solution that should compliment Andrew Clark's answer (props for using itertools!). His answer returns each value once like this:
iterable = [0, 1, 2, 3, 4, 5, 6,...]
n = 2
grouper(n, iterable, fillvalue=None)
--> [(0, 1), (2, 3), (3, 4), (5, 6),...]
In the code below each value will appear in n sub-sequences. Like this:
def moving_window(n, iterable):
start, stop = 0, n
while stop <= len(iterable):
yield iterable[start:stop]
start += 1
stop += 1
--> [(0, 1), (1, 2), (2, 3), (3, 4),...]
A common application for this type of 'moving window' or 'kernel' is moving averages in the sciences and finance.
Also note that the yield statement allows each sub-sequence to be created as it's needed and not stored in memory.
Upvotes: 4