0

Here is the code

def list_all(obj):
    """
    Return a list of all non-list elements in obj or obj's sublists, if obj is a list. Otherwise, return a list containing obj.

    @param list|object obj: object to list
    @rtype: list

    >>> obj = 17
    >>> list_all(obj)
    [17]
    >>> obj = [1, 2, 3, 4]
    >>> list_all(obj)
    [1, 2, 3, 4]
    >>> obj = [[1, 2, [3, 4], 5], 6]
    >>> all([x in list_all(obj) for x in [1, 2, 3, 4, 5, 6]])
    True
    >>> all ([x in [1, 2, 3, 4, 5, 6] for x in list_all(obj)])
    True
    """
    if not isinstance(obj, list):
        return obj
    else:
        return [list_all(x) for x in obj]

When I tried print(list_all([[2,3],[4,5]])), it prints out the exactly same input, meaning the code does nothing at all. I think the problem is the [] bracket but I can't think of a way to eliminate. Could someone help?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
CoolKid
  • 143
  • 9

3 Answers3

2

This should help:

def list_all(obj):
    if not isinstance(obj, list):
        return [obj]
    else:
        return [item for sublist in obj for item in list_all(sublist)]        

print(list_all(1))                   # Scalar
print(list_all([[2,3],[4,5]]))       # One level
print(list_all([[2,[3,2]],[4,5]]))   # Two levels

Output:

 [1]
 [2, 3, 4, 5]
 [2, 3, 2, 4, 5]
Juan Leni
  • 6,982
  • 5
  • 55
  • 87
  • That flattens one layer of nesting, and doesn't handle the case where the top level list has some scalar values and some sub-lists. It's not a meaningful improvement on the [`flatten` recipe from the `itertools` documentation](https://docs.python.org/3/library/itertools.html#itertools-recipes); the only difference is that it returns scalars unmodified. – ShadowRanger Mar 02 '16 at 17:46
  • @ShadowRanger I disagree. The example already shows two levels of nesting. – Juan Leni Mar 02 '16 at 17:48
  • mtk99, I don't understand about the downvote. Looks fine for me, I going to vote up your solution. If it is wrong, people should explaing here. – dani herrera Mar 02 '16 at 17:51
  • @mtk99: Your original post (not shown in edit history because no votes or comments had been made, but still cached in my browser on another tab) had the comprehension as `return [item for sublist in obj for item in sublist]`, with no recursive `list_all` call, and the only example was running on `[[2,3],[4,5]]` which didn't display non-uniform nesting. That's what I was objecting to; you changed it, so it now works, but you know as well as I do what I was commenting on. – ShadowRanger Mar 02 '16 at 17:55
  • @ShadowRanger Your comment came several minutes after my final change. No worries this is not a competition – Juan Leni Mar 02 '16 at 18:02
  • @mtk99: Yar (though I couldn't have commented more than a minute or so after the change that added recursion). I don't think the answer as written now is bad ([though `itertools.chain.from_iterable` could speed it up mildly](http://stackoverflow.com/a/953097/364696)), and I'm removing the down-vote. – ShadowRanger Mar 02 '16 at 18:09
2

Rather than constructing a bunch of intermediate lists, consider using a generator function. Particularly in Python 3.3+, the code is very simple:

def flatten_lists(items):
    if isinstance(items, list):
        for x in items:
            yield from flatten_lists(x)
    else:
        yield items

If you need the ultimate result to be a list, either you let callers wrap in list() themselves, or you define a wrapper to call the generator and convert it to a list for them:

def list_all(items):
    return list(flatten_lists(items))

It dramatically reduces the allocator churn involved in creating and disposing of all the intermediate lists compared to non-generator based solutions.

Example:

>>> list_all([1, [2, [3, [4, 5], 6]]])
[1, 2, 3, 4, 5, 6]
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
0

Something like this, sorry not tested but is trivial ....

def list_all(obj):

    result= []
    if not isinstance(obj, list):
        result.append(obj)
    else:
        for x in obj:
            result += list_all( x )
    return result
dani herrera
  • 48,760
  • 8
  • 117
  • 177