Cycling through iterables diagonally

Jussi Piitulainen jussi.piitulainen at helsinki.fi
Fri Feb 26 04:21:34 EST 2016


Pablo Lucena writes:

> Hello,
>
> I am trying to accomplish the following:
>
> Say I have a group of 4 lists as follows:
>
> l1 = ['a1', 'a2', 'a3', 'a4']
> l2 = ['b1', 'b2', 'b3', 'b4']
> l3 = ['c1', 'c2', 'c3', 'c4']
> l4 = ['d1', 'd2', 'd3', 'd4']
>
> I would like to cycle through these lists "diagonally" in groups of
> len(list) (in this example, each list has 4 items).
>
> cycle1: a1, b2, b3, b4
> cycle2: a2, b3, c4, d1
> cycle3: a3, b4, c1, d2
> cycle4: a4, b1, c2, d3

First line should be a1, b2, c3, d4, right?

> The way I thought about doing this is as follows:
>
> from collections import deque
> from itertools import cycle

[...]

> Prior to this I was mucking around with index counting while looping,
> and popping lists out of a deque, popping an item out of the list, and
> appending the list back into the deque during each iteration.
>
> Is there a better/cleaner way to do this? I was hoping for some cool
> itertools logic =)

Here's a combination of itertools (chaining of slices for the rotations)
and builtin machinery (argument spreading, a generator expression, zip,
enumerate) that seems to have the desired effect.

from itertools import chain, islice

l1 = ['a1', 'a2', 'a3', 'a4']
l2 = ['b1', 'b2', 'b3', 'b4']
l3 = ['c1', 'c2', 'c3', 'c4']
l4 = ['d1', 'd2', 'd3', 'd4']

n = 4
chainfrom = chain.from_iterable
print(*chainfrom(zip(*(chain(islice(m, k, n),
                             islice(m, 0, k))
                       for k, m
                       in enumerate([l1, l2, l3, l4])))))

# prints: a1 b2 c3 d4 a2 b3 c4 d1 a3 b4 c1 d2 a4 b1 c2 d3


More information about the Python-list mailing list