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.
You can loop through the list items by using a while loop. Use the len() function to determine the length of the list, then start at 0 and loop your way through the list items by referring to their indexes.
Use zip() and a for-loop to iterate over two lists Call zip(iter1 iter2) to create an iterator that aggregates corresponding elements from lists iter1 and iter2 together. Use a for-loop to iterate over this iterator, stopping at the length of the shorter list.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With