kram1032 kram1032 - 6 months ago 12
Python Question

traverse nested list with single for

I have a bunch of lists in lists. The nesting depth is determined at run-time and I only want to access them to a specific (run-time decided) depth to manipulate what ever is at that level in an arbitrary way.

Ideally I'd like to be able to do this as simply as:

for x in access_list(nested_list, d)
# do stuff at nesting-depth d


What
access_list
should do:

>>> mylist = [[[0, 1], [2, 3]], [[4, 5], [6, 7]]]
>>> for d in range(4):
... for l in access_list(mylist, d):
... print((d, l))
(0, [[[0, 1], [2, 3]], [[4, 5], [6, 7]]])
(1, [[[0, 1], [2, 3]])
(1, [[4, 5], [6, 7]]])
(2, [0, 1])
(2, [2, 3])
(2, [4, 5])
(2, [6, 7])
(3, 0)
(3, 1)
(3, 2)
(3, 3)
(3, 4)
(3, 5)
(3, 6)
(3, 7)


My attempt turned out to do essentially nothing:

def access_list(lists, d):
if not d:
return lists
return [access_list(_list, d-1) for _list in lists]


It just returns the whole list structure again.
What could I do to make this work?

Answer

This generator function should work for nested lists and saves memory as it doesn't build a list itself, but lazily produces the items:

def access_list(nested_list):
    if not isinstance(nested_list, list):
    # if not isinstance(nested_list, (list, set)): you get the idea
        yield nested_list
    else:
        for item in nested_list:
            for x in access_list(item):
                yield x
            # in Python 3, you can replace that loop by:
            # yield from access_list(item)
    return

> l = [1, 2, [3, [4, 5], 6]]
> list(access_list(l))
[1, 2, 3, 4, 5, 6]

If you want access to nesting depth, the following will produce pairs (item, depth):

def access_list(nested_list, d=0):
    if not isinstance(nested_list, list):
        yield nested_list, d
    else:
        for item in nested_list:
            for x in access_list(item, d=d+1):
                yield x
    return

> l = [1, 2, [3, [4, 5], 6]]
> list(access_list(l))
[(1, 1), (2, 1), (3, 2), (4, 3), (5, 3), (6, 2)]
Comments