Welcome, guest | Sign In | My Account | Store | Cart

Interleaving can be thought of as two-dimensional iteration over a sequence of sequences, pulling the ith element from all n seqeuences before moving on to the ith + 1 element. Interleave does not truncate to the shortest list unlike flatten(zip(seqA, seqB)) [see flatten() recipe]. Here's an illustration:

a = [1,3,5]
b = [2,4,6,8]
c = ["x","y","z"]

interleave(a,b,c) will return:

[1, 2, "x", 3, 4, "y", 5, 6, "z", 8]
Python, 7 lines
1
2
3
4
5
6
7
def interleave(*args):
    for idx in range(0, max(len(arg) for arg in args)):
        for arg in args:
            try:
                yield arg[idx]
            except IndexError:
                continue

There are a wide variety of problems from accounting to table pretty-printing which can benefit from interleaving.

I initially wrote interleave() so that payments could be distributed evenly across line items on an invoice. Here's the scenario:

Suppose an Invoice contains two ProductOrders which each comprise two line items with accounting codes for ItemPrice and Shipping respectively. Now, as you are applying payments to this invoice, you may want to apply payments to the ItemPrice code of the first order, then to the Shipping code of the first order, and then move on to the next order. One way of handling this situation is to split line items into two separate lists by accounting code and then apply payments to the interleaved list. For example:

Order 1: ItemPrice 22.99 Shipping 0.10 Order 2: ItemPrice 43.99 Shipping 0.25

ItemPrice = [22.99, 43.99] Shipping = [0.10, 0.25]

Now when we apply_payment(interleave(ItemPrice, Shipping)) we will be distributing according to our business rule.

5 comments

Raymond Hettinger 14 years, 7 months ago  # | flag

Similar to the roundrobin() recipe. The roundrobin recipe provides similar capabilities but works lazily and doesn't require indexing. See the collections.deque() docs at http://docs.python.org/lib/deque-recipes.html )

Steven Bethard 14 years, 7 months ago  # | flag

zip/izip. If your sequences are all the same length, a simple application of zip or izip gets you the same thing:

>>> a = [1,3,5]
>>> b = [2,4,6,8]
>>> c = ["x","y","z"]
>>> [item for items in itertools.izip(a, b, c) for item in items]
[1, 2, 'x', 3, 4, 'y', 5, 6, 'z']
Richard Harris (author) 14 years, 7 months ago  # | flag

ANN: Device for converting rotational motion into translational by means of a circular shaped mass about an axle. Re-inventing the wheel is fun sometimes. :) The deque roundrobin() recipe is exactly what I needed. Thanks.

Alain Pointdexter 14 years, 7 months ago  # | flag

a simpler way. a = [1,3,5]

b = [2,4,6,8]

c = ["x","y","z"]

print [ y for x in map(None,a,b,c) for y in x if y]

Matthew Sherborne 14 years, 7 months ago  # | flag

wow. That's just freaky man!

So an expansion of that, without itertools would be:

a = [1,3,5]
b = [2,4,6,8]
c = ["x","y","z"]

result = []
for items in zip(a, b, c):
    for item in items:
        result.append(item)

print result == [item for items in zip(a,b,c) for item in items]
Created by Richard Harris on Wed, 18 Apr 2007 (PSF)
Python recipes (4591)
Richard Harris's recipes (3)

Required Modules

  • (none specified)

Other Information and Tasks