Wednesday, May 10th, 2006

Flattening lists in Python

Filed under: — Daniel Lemire @ 14:49

Can anyone do better than this ugly hack?

def flatten(x):
flat = True
ans = []
for i in x:
if ( i.__class__ is list):
ans = flatten(i)
else:
ans.append(i)
return ans

Update. I like this solution proposed by one of the commenters (sweavo):

def flatten(l):
if isinstance(l,list):
return sum(map(flatten,l))
else:
return l

Can anyone do better?

See also my posts YouTube scalability, Yield returns are not esoteric anymore, Efficient FIFO/Queue data structure in Python and Autocompletion in the Python console.

Subscribe to this blog
in a reader
or by Email.

10 Comments »

  1. I think you just want isinstance, but here’s a nice recursive function:

    How about:

    def flatten(x):
    if not isinstance(x,list):
    return x
    elif len(x) is 0:
    return []
    elif isinstance(x[0],list):
    return flatten(x[0]) + flatten(x[1:])
    else:
    return [x[0]] + flatten(x[1:])

    Comment by Will — 10/5/2006 @ 18:20

  2. No, Daniel, I don’t know a better way ATM.
    A possible reason for this obvious lack could be that the Python community likes to have functions with clear semantics. For example, sometimes I want to flatten only one level, or different types, for example, given this input:

    [[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)]

    One could imagine the following outputs:

    # flattened one level:
    [[1, 2, 3], (42, None), 4, 5, 6, 7, MyVector(8,9,10)]

    # flattened all lists:
    [1, 2, 3, (42, None), 4, 5, 6, 7, MyVector(8,9,10)]

    # flattened all lists and tuples:
    [1, 2, 3, 42, None, 4, 5, 6, 7, MyVector(8,9,10)]

    # flattened all iterables:
    [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]

    @Will: Daniels version looks more optimized to me. The question whether isinstance is better than checking the class is another possible implementation decision, e.g. given the following definition of MyVector:

    class MyVector(list):
    def __init__(self, *args):
    list.__init__(self, args)

    Your version would handle it the same as a list, while daniels would not flatten it.

    For the record, the above corresponds to the following checks in Daniel’s flatten function:
    if i.__class__ is list:
    if i.__class__ in (list, tuple):
    if isinstance(i, list):
    if isinstance(i, (list, tuple)):
    if hasattr(i, “__iter__”):
    AFAICS the simplest way to flatten only one layer is to change the recursive call to “list” instead of “flatten”.

    Comment by Hans Meine — 1/6/2006 @ 12:20

  3. Here is a version from the mailing list:

    def flatten(seq):
    res = []
    for item in seq:
    if (isinstance(item, (tuple, list))):
    res.extend(flatten(item))
    else:
    res.append(item)
    return res

    Another version is listed a in recipe 363051:

    import sys
    def flatten(inlist, ltype=(list,tuple), maxint=sys.maxint):
    try:
    for ind in xrange(maxint):
    while isinstance(inlist[ind], ltype):
    inlist[ind:ind+1] = list(inlist[ind])
    except IndexError:
    pass
    return inlist

    Comment by Jordan Callicoat — 4/9/2006 @ 2:33

  4. def flatten(l):
    if isinstance(l,list):
    return sum(map(flatten,l))
    else:
    return l

    Comment by sweavo — 11/7/2007 @ 11:45

  5. def flatten(x):
    result = []
    for v in x:
    if hasattr(v, ‘__iter__’) and not isinstance(v, basestring):
    result.extend(flatten(v))
    else:
    result.append(v)
    return result

    Comment by anon — 14/12/2007 @ 8:23

  6. As far as I can tell, several of the functions presented above do not work, including sweavo’s. Thie function below does work.

    def flatten(x):
    ans = []
    for i in range(len(x)):
    if isinstance(x[i],list):
    ans = x[:i]+flatten(x[i])+x[i+1:]
    else:
    ans.append(x[i])
    return ans

    Comment by BioStatMatt — 31/1/2008 @ 16:57

  7. Sorry, my previous post didn’t work either. But this one does. Honest.

    def flatten(x):
    ans = []
    for i in range(len(x)):
    if isinstance(x[i],list):
    ans.extend(flatten(x[i]))
    else:
    ans.append(x[i])
    return ans

    Comment by BioStatMatt — 31/1/2008 @ 17:10

  8. A modified version of sweavo’s post (which didn’t work for me either):

    def flatten(l):
    if isinstance(l,list):
    return sum(map(flatten,l),[])
    else:
    return [l]

    Comment by timv — 29/2/2008 @ 21:59

  9. What about using the reduce-function?

    E.g.

    def flatten(l):
    return reduce(operator.add, l)

    Obviously this requires the operator-module import.

    Comment by Markus — 5/3/2008 @ 11:29

  10. There is a problem with most of these in that they’re recursive. Why is it a problem? Because python’s stack is going to blow up when the depth of the list is something ridiculously large.

    So, we must do this with iteration. One way is to iterate over the list until no lists remain:

    def flatten(lst):
    has_lists = True
    while has_lists:
    tmp_lst = []
    has_lists = False
    for elt in lst:
    if isinstance(elt, list):
    tmp_lst += elt
    has_lists = True
    else:
    tmp_lst.append(elt)
    lst = tmp_lst
    return lst

    Of course, you’re doing more iteration that you need to if the list contains elements of variable list depth. But since function calls are expensive in Python, it may actually be faster than recursion.

    Comment by Bob the Chef — 15/5/2008 @ 16:35

RSS feed for comments on this post.

Leave a comment

Warning: When entering a long comment, please ensure that you make copy of your text prior to submitting it. If the server should fail or if you hit a bug, you might lose your work. I am not responsible for your lost effort.

To spammers: I carefully review every single post and make sure that spam gets deleted. You are wasting your time if you are manually entering spam using this form. Read my terms of use to see what I consider to be abusive.

Example: I + II + IX= XII. (Yes, you have to enter a roman numeral.)

« Blog's main page

24 queries. 0.160 seconds. Valid XHTML

Powered by WordPress

Subscribe to this blog in a reader or by Email.