How can I flatten lists without splitting strings?

I’d like to flatten lists that may contain other lists without breaking strings apart. For example:

In [39]: list( itertools.chain(*["cat", ["dog","bird"]]) )
Out[39]: ['c', 'a', 't', 'dog', 'bird']

and I would like

['cat', 'dog', 'bird']

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Solution:

def flatten(foo):
    for x in foo:
        if hasattr(x, '__iter__') and not isinstance(x, str):
            for y in flatten(x):
                yield y
        else:
            yield x

Old version for Python 2.x:

def flatten(foo):
    for x in foo:
        if hasattr(x, '__iter__'):
            for y in flatten(x):
                yield y
        else:
            yield x

(In Python 2.x, strings conveniently didn’t actually have an __iter__ attribute, unlike pretty much every other iterable object in Python. Note however that they do in Python 3, so the above code will only work in Python 2.x.)

Method 2

A slight modification of orip’s answer that avoids creating an intermediate list:

import itertools
items = ['cat',['dog','bird']]
itertools.chain.from_iterable(itertools.repeat(x,1) if isinstance(x,str) else x for x in items)

Method 3

a brute force way would be to wrap the string in its own list, then use itertools.chain

>>> l = ["cat", ["dog","bird"]]
>>> l2 = [([x] if isinstance(x,str) else x) for x in l]
>>> list(itertools.chain(*l2))
['cat', 'dog', 'bird']

Method 4

def squash(L):
    if L==[]:
        return []
    elif type(L[0]) == type(""):
        M = squash(L[1:])
        M.insert(0, L[0])
        return M
    elif type(L[0]) == type([]):
        M = squash(L[0])
        M.append(squash(L[1:]))
        return M

def flatten(L):
    return [i for i in squash(L) if i!= []]

>> flatten(["cat", ["dog","bird"]])
['cat', 'dog', 'bird']

Hope this helps

Method 5

Here’s a one-liner approach:

[item for sublist in [[item] if type(item) is not list else item for item in list1] for item in sublist]

Method 6

With the reduce function from the functools library you can do it like this:

import functools
items = ['cat',['dog','bird']]
print(functools.reduce(lambda a, b: [a] + b, items))

Method 7

a lambda function approach that works for more than two levels of hierarchy

>>> items = ['cat',['dog','bird',['fish']]] 
>>> flatten = lambda y: [k for j in ([i] if not isinstance(i,list) else flatten(i) for i in y) for k in j]
>>> flatten(items)
['cat', 'dog', 'bird', 'fish']


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x