Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Outerzip / zip longest function (with multiple fill values)

Tags:

python

Is there a Python function an "outer-zip", which is a extension of zip with different default values for each iterable?

a = [1, 2, 3]   # associate a default value 0
b = [4, 5, 6, 7] # associate b default value 1

zip(a,b)  # [(1, 4), (2, 5), (3, 6)]

outerzip((a, 0), (b, 1)) = [(1, 4), (2, 5), (3, 6), (0, 7)]
outerzip((b, 0), (a, 1)) = [(4, 1), (5, 2), (6, 3), (7, 1)]

I can almost replicate this outerzip function using map, but with None as the only default:

map(None, a, b) # [(1, 4), (2, 5), (3, 6), (None, 7)]

Note1: The built-in zip function takes an arbitrary number of iterables, and so should an outerzip function. (e.g. one should be able to calculate outerzip((a,0),(a,0),(b,1)) similarly to zip(a,a,b) and map(None, a, a, b).)

Note2: I say "outer-zip", in the style of this haskell question, but perhaps this is not correct terminology.

like image 629
Andy Hayden Avatar asked Oct 26 '12 11:10

Andy Hayden


3 Answers

It's called izip_longest (zip_longest in python-3.x):

>>> from itertools import zip_longest
>>> a = [1,2,3]
>>> b = [4,5,6,7]
>>> list(zip_longest(a, b, fillvalue=0))
[(1, 4), (2, 5), (3, 6), (0, 7)]
like image 132
SilentGhost Avatar answered Sep 19 '22 21:09

SilentGhost


You could modify zip_longest to support your use case for general iterables.

from itertools import chain, repeat

class OuterZipStopIteration(Exception):
    pass

def outer_zip(*args):
    count = len(args) - 1

    def sentinel(default):
        nonlocal count
        if not count:
            raise OuterZipStopIteration
        count -= 1
        yield default

    iters = [chain(p, sentinel(default), repeat(default)) for p, default in args]
    try:
        while iters:
            yield tuple(map(next, iters))
    except OuterZipStopIteration:
        pass


print(list(outer_zip( ("abcd", '!'), 
                      ("ef", '@'), 
                      (map(int, '345'), '$') )))
like image 34
kennytm Avatar answered Sep 20 '22 21:09

kennytm


This function can be defined by extending each inputted list and zipping:

def outerzip(*args):
    # args = (a, default_a), (b, default_b), ...
    max_length = max( map( lambda s: len(s[0]), args))
    extended_args = [ s[0] + [s[1]]*(max_length-len(s[0])) for s in args ]
    return zip(*extended_args)

outerzip((a, 0), (b, 1)) # [(1, 4), (2, 5), (3, 6), (0, 7)]
like image 37
Andy Hayden Avatar answered Sep 20 '22 21:09

Andy Hayden