Is there a way of avoiding so many list(chain(*list_of_list))?

112 views Asked by At

If I have a list of list of list of tuples of two strings. I want to flatten it out to a non-nested list of tuples, I could do this:

>>> from itertools import chain
>>> lst_of_lst_of_lst_of_tuples = [ [[('ab', 'cd'), ('ef', 'gh')], [('ij', 'kl'), ('mn', 'op')]], [[('qr', 'st'), ('uv', 'w')], [('x', 'y'), ('z', 'foobar')]] ]
>>> lllt = lst_of_lst_of_lst_of_tuples
>>> list(chain(*list(chain(*lllt))))
[('ab', 'cd'), ('ef', 'gh'), ('ij', 'kl'), ('mn', 'op'), ('qr', 'st'), ('uv', 'w'), ('x', 'y'), ('z', 'foobar')]

But is there another way of unpacking to the non-nested list of tuples withou the nested list(chain(*lst_of_lst))?

2

There are 2 answers

0
jme On BEST ANSWER

You could keep unpacking until you hit tuples:

def unpack_until(data, type_):
    for entry in data:
        if isinstance(entry, type_):
            yield entry
        else:
            yield from unpack_until(entry, type_)

Then:

>>> list(unpack_until(lllt, tuple))
[('ab', 'cd'),
 ('ef', 'gh'),
 ('ij', 'kl'),
 ('mn', 'op'),
 ('qr', 'st'),
 ('uv', 'w'),
 ('x', 'y'),
 ('z', 'foobar')]
0
user2357112 On

You don't need to call list before unpacking an iterator:

list(chain(*chain(*lllt)))

It may be better to use chain.from_iterable rather than unpacking, to work with iterators instead of materializing them into tuples with *:

flatten = chain.from_iterable
list(flatten(flatten(lllt)))