Question

I'm doing an exercise to flatten nested lists. The code works in console but it doesn't work when its in a file. I have no idea what's going on. :(

def flatten(nested):
    """
            >>> flatten([2, 9, [2, 1, 13, 2], 8, [2, 6]])
            [2, 9, 2, 1, 13, 2, 8, 2, 6]
            >>> flatten([[9, [7, 1, 13, 2], 8], [7, 6]])
            [9, 7, 1, 13, 2, 8, 7, 6]
            >>> flatten([[9, [7, 1, 13, 2], 8], [2, 6]])
            [9, 7, 1, 13, 2, 8, 2, 6]
            >>> flatten([[5, [5, [1, 5], 5], 5], [5, 6]])
            [5, 5, 1, 5, 5, 5, 5, 6]
    """
    simple = []

    for x in nested:
            if type(x) == type([]):
                    for y in x:
                            simple.append(y)
            else:
                    simple.append(x)
    return simple



if __name__ == '__main__':
    import doctest
    doctest.testmod()

I first tried to solve this exercise recursively but decided to try it iterative first.

edit: When executed in the file it just prints out the original function argument TIA

Was it helpful?

Solution

The problem is that your flatten function only flattens one level. The reason why doctest is printing out errors is because it is indeed erroring. They are not what you passed in.

File "test.py", line 5, in __main__.flatten
Failed example:
    flatten([[9, [7, 1, 13, 2], 8], [7, 6]])
Expected:
    [9, 7, 1, 13, 2, 8, 7, 6]
Got:
    [9, [7, 1, 13, 2], 8, 7, 6]

You should investigate a recursive approach that instead of appending y--- you call flatten on y as well. if type(x) != type([]) can be your base case.

OTHER TIPS

If you want to "cheat" you could do this:

L = [1, 2, [3, 4], 5, 6, [7, 8, [9, 10, 11]]]
s = repr(L)
s = '[' + s.replace('[','').replace(']','') + ']'
L = eval(s)

I'm kind of curious how fast that would be compared to the "normal" flattening operation...


Edit:

A cursory test shows that the cheating method takes nearly constant time regardless of data complexity, while a recursive solution increases in time.

Larger

Cheat: 7.13282388182
Recurse: 2.84676811407

Smaller

Cheat: 7.08800692623
Recurse: 0.486098086038

Here's my code (and I'm really curious about larger data sets!):

import timeit

L = [1,2,3,
     [46, 100000, 20, 9, 
      [1,2,3, 
       [9, 23, 24, 
        [9, 23, 24, 
         [9, 23, 24, 
          [9, 23, 24, 
           [9, 23, 24, [9, 23, 24, [13], 12],4]]]], 26]]]]

L2 = [1,2,3, [4,5,6]]

def flattencheat(mylist):
    s = repr(L)
    s = '[' + s.replace('[', '').replace(']', '') + ']'
    return eval(s)

def flattencurse(mylist):
    newlist = []
    for val in mylist:
        if not hasattr(val, '__iter__'):
            newlist.append(val)
        else:
            newlist.extend(flattencurse(val))

    return newlist

print "Cheat: ", timeit.timeit('flattencheat(L)', "from __main__ import flattencheat, L", number=100000)
print "Recurse: ", timeit.timeit('flattencurse(L)', 
                                 'from __main__ import flattencurse, L',
                                 number = 100000)
print "Cheat: ", timeit.timeit('flattencheat(L2)', "from __main__ import flattencheat, L2", number=100000)
print "Recurse: ", timeit.timeit('flattencurse(L2)', 
                                 'from __main__ import flattencurse, L2',
                                 number = 100000)

Your application is not doing nothing.

Input:  [3,[5, [5, [1, 5], 5], 5], [5, 6]]
Output: [3, 5, [5, [1, 5], 5], 5 ,  5, 6]

You need to keep flattening until it completes, e.g. using recursion. Simple but with one extra pass over your data: If type(x) == type([]), return flatten(simple) instead of simple at the end of the function.

I think that @orangeoctopus's answer is correct, but doesn't capture the "why does it work in the console" question. I'm going to make a guess:

It doesn't work in the console. I think you tested with a subset of the input that happens to work. For example,

>>> flatten([2, 9, [2, 1, 13, 2], 8, [2, 6]])
[2, 9, 2, 1, 13, 2, 8, 2, 6]

works!

But

>>> flatten([[9, [7, 1, 13, 2], 8], [7, 6]])
[9, [7, 1, 13, 2], 8, 7, 6]

does not so much.

My answer is pretty similiar to @DonaldMiner's (In fact I started out with that as well), but then noticed that if a string in the list contains "[" or "]", it would fail.

E.g. won't work here:

["Hello", "World", "A", "[Menu]"]

I wrote a similiar function (a little more complex, and only removes the [ and ] if it is not in a string in the list.

from ast import literal_eval
def flatten(to_squash):
    """This will flatten a list, no matter the recursion limit."""
    instring = False
    squashed = []
    index = 0
    print(repr(to_squash))
    for char in repr(to_squash):
        if char == '"' or char == "'":
            instring = not instring
            print(instring, char)
            squashed.append(char)
        elif not instring and char == "[" or not instring and char == "]":
            pass
        else:
            squashed.append(char)
        index += 1

    print("".join([item.strip() for item in squashed]))
    return literal_eval("".join(squashed))

You could of course also use eval(), but only if you dont read in the list from somwhere (It is made by the program). Otherwise, someone can just enter a command, and python will execute it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top