9

I have a list of numbers: testList = [1, [1], [12], 2, 3]

I want it to become: flatList = [1, 1, 12, 2, 3]

Using a typical list comprehension such as below is not working.

flatList = [val for sublist in testList for val in sublist]
TypeError: 'int' object is not iterable

I suspected it is because the un-nested items are being treated as iterable sublists, so I tried this:

flatList = [val if isinstance(sublist, int) == False else val for sublist in testlist for val in sublist]

But I am unclear on the syntax, or if there is some better way to do this. Trying to remove val from the else clause means val is undefined. As is, it still gives me the same TypeError.

The code below does work for me, but I am interested to see if it can be done in list comprehension style, and people's opinions on that.

for sublist in testlist:
    if type(sublist) == int:
        flat.append(sublist)
    else:
        for val in sublist:
            flat.append(val)
print(flat)

>>>[1, 1, 12, 2, 3]
4
  • Can the sub-list contain more than one integer? Commented Nov 22, 2017 at 11:05
  • 1
    In else block, you can replace the for with flat.extend(sublist) Commented Nov 22, 2017 at 11:25
  • @AidanHorton It can contain more than one integer. Commented Nov 22, 2017 at 12:28
  • @stack_n_queue thanks for pointing that out. Commented Nov 22, 2017 at 12:28

2 Answers 2

9

Since you're using Python 3, you can take advantage of yield from with a recursive function. It has been introduced in Python 3.3.

As a bonus, you can flatten arbitrary nested lists, tuples, sets or ranges:

test_list = [1, [1], [12, 'test', set([3, 4, 5])], 2, 3, ('hello', 'world'), [range(3)]]

def flatten(something):
    if isinstance(something, (list, tuple, set, range)):
        for sub in something:
            yield from flatten(sub)
    else:
        yield something


print(list(flatten(test_list)))
# [1, 1, 12, 'test', 3, 4, 5, 2, 3, 'hello', 'world', 0, 1, 2]
print(list(flatten('Not a list')))
# ['Not a list']
print(list(flatten(range(10))))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Here's another example with a debug line:

def flatten(something, level=0):
    print("%sCalling flatten with %r" % ('  ' * level, something))
    if isinstance(something, (list, tuple, set, range)):
        for sub in something:
            yield from flatten(sub, level+1)
    else:
        yield something

list(flatten([1, [2, 3], 4]))
#Calling flatten with [1, [2, 3], 4]
#  Calling flatten with 1
#  Calling flatten with [2, 3]
#    Calling flatten with 2
#    Calling flatten with 3
#  Calling flatten with 4
Sign up to request clarification or add additional context in comments.

4 Comments

I'd name something iterable, instead.
Alright, it's up to you, but I'd raise a TypeError if something isn't an iterable (since it makes no sense to call flatten on integers and strings?)
Right, I get it now. I often have trouble wrapping my head around recursion :)
Wow, very useful. Not a function I was familiar with, and it's good to see how isinstance() can be used with multiple types.
2

If the sublists always contain only one item then

flatList = [item[0] if isinstance(item, list) else item for item in testList]

1 Comment

Thanks, useful in those cases.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.