0

I have a nested list, like this one for example:

 test = [[15, [7, [None], [11, [None], [13, [None], [None]]]], [None]], [20, [None], [None]]] 

I was wanting to create another list from this with only integers contained in the nest. Which would return this:

[15, 7, 11, 13, 20]

I have made this recursive function to do what I needed to accomplish but, I couldn't help to think this isn't the best way to go about it. Is there a more pythonic or efficient way to do it?

def nest_search(nest, hold=[]):
    for item in nest:
        if isinstance(item, int):
            hold.append(item)
        if isinstance(item, list):
            nest_search(item, hold)
    return hold

>>> print nest_search(test)
[15, 7, 11, 13, 20]
tijko
  • 7,599
  • 11
  • 44
  • 64
  • 1
    the operation you are looking for is called flatten. search for flatten list in python. – Lynch Nov 23 '12 at 03:38
  • [Flatten](http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python) and then filter – Waleed Khan Nov 23 '12 at 03:39
  • I've flatten lists before, I don't think this will work with the example `list` I posted? – tijko Nov 23 '12 at 03:46
  • it should ... but you are close i modified your function and now it should work – Joran Beasley Nov 23 '12 at 03:48
  • 1
    All of the "clean" (one liners/pythonic) flatten algorithms I've seen only work with lists of lists, never with lists of arbitrary depth. – Tim Nov 23 '12 at 03:50
  • @Tim so you are saying this function might be the best option? – tijko Nov 23 '12 at 03:51
  • @tijko: What you have now is the best way I can think of. Although, you will have issues because you use a [mutable object as a default argument](http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument). – Tim Nov 23 '12 at 03:54
  • you can certainly flatten arbitrary depth lists but the function would probably be the same number of lines as your solution ... then a second filter (so increased time complexity) – Joran Beasley Nov 23 '12 at 03:57

2 Answers2

2

The only thing I see that's unpythonic is the default argument. See this question for why that won't work the way your expect.

Here's how I'd fix it:

def nest_search(nest, hold=None):
    if hold is None:
        hold = []
    for item in nest:
        if isinstance(item, int):
            hold.append(item)
        if isinstance(item, list):
            nest_search(item, hold)
    return hold

An alternative implementation would be to make the function a generator, which yields the values one by one, rather than adding them to a list that it returns at the end. (If you do need a list, just wrap the generator call in the list constructor).

def nest_search_gen(nest):
    for item in nest:
        if isinstance(item, int):
            yield item
        if isinstance(item, list):
            yield from nest_search_gen(item)

This uses the new yield from syntax introduced in Python 3.3. If you are using an earlier version, you can get the same effect by replacing the last line with:

for i in nest_search_gen(item):
    yield i
Community
  • 1
  • 1
Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • I like the idea of using a generator and, I'll look into my mistake with the default argument. – tijko Nov 23 '12 at 04:01
1

Using a flatten solution posted here, you could try something like the following.

>>> def flatten(x):
    try:
      it = iter(x)
    except TypeError:
      yield x
    else:
      for i in it:
        for j in flatten(i):
          yield j
>>> filter(bool, flatten(test))
[15, 7, 11, 13, 20]

I think the use of two separate functions flatten and filter is clearer, and you encourage modularity, allowing one to be used without the other.

Community
  • 1
  • 1
Aesthete
  • 18,622
  • 6
  • 36
  • 45
  • this `flatten` solution is very similar to what @Blckknght had suggested by tweaking the recursive function I posted to a generator but, the use of `try/except` is generally looked at as a better option than `isinstance` right? – tijko Nov 23 '12 at 04:23
  • It's a case of **LBYL** vs. **EAFP**, and either is acceptable. – Aesthete Nov 23 '12 at 04:28
  • The only problem with this `flatten` code, and a reason that LBYL may be better for this particular problem, is that strings look like infinitely nested sequences. That is, they're iterable, and all their member items are also iterable, since they're (one character) strings. Try: `flatten(["AB", ["C"]])` and it'll recurse on "A" all the way to the recursion limit. – Blckknght Nov 24 '12 at 02:20