Flatten list of list of strings

Viewed 4854

I have this list:

list1 = ["['word']", "['second', 'first']", "['first']"]

You can see it's not exactly a list of list of strings as the items were converted into strings.

I want to have this flattened output:

list2 = ['word', 'second', 'first', 'first']

Just a simple list of strings.

I 've tried using this way:

list2 = [ x.strip('[]') for x in list1]

But the issue is with the second item still considered as one item:

["'word'", "'second', 'first'", "'first'"]

So I tried splitting by comma:

list3 = [item.split(',') for x in list2 for item in x]

But it gave this output:

[["'"],
 ['w'],
 ['o'],
 ['r'],
 ['d'],
 ["'"],
 ["'"],
 ['s'],
 ['e'],
 ['c'],
 ['o'],
 ['n'],
 ['d'],
 ["'"],
 ['', ''],
 [' '],
 ["'"],
 ['f'],
 ['i'],
 ['r'],
 ['s'],
 ['t'],
 ["'"],
 ["'"],
 ['f'],
 ['i'],
 ['r'],
 ['s'],
 ['t'],
 ["'"]]

Any idea how to fix this?

UPDATES - SOLUTION

With help from @AKX

list2 = [ast.literal_eval(item) for item in list1]

Then using this function:

def flatten(lst):
    for el in lst:
        if isinstance(el, list):
            yield from el
        else:
            yield el

list3 = flatten(list2)
list(list3)

This gives:

['word', 'second', 'first', 'first']

UPDATE 2

An optimized and alternative solution from @waynelpu instead of flatten function, just use:

list2 = [inner for item in list1 for inner in ast.literal_eval(item)] 
5 Answers

If you have a list of strings of Python expressions that represent lists (how's that for a nested clause), you will have to use ast.literal_eval() to get back to reality, as it were.

>>> import ast
>>> list1 = ["['word']", "['second', 'first']", "['first']"]
>>> list2 = [ast.literal_eval(item) for item in list1]
[['word'], ['second', 'first'], ['first']]

Using ast.literal_eval(), as opposed to the dangerous eval() that you shouldn't use is safe, as it only evaluates literals that can have no side effects.

list1 = ["['word']", "['second', 'first']", "['first']"]
new_lst = [sub_val for val in list1 for sub_val in eval(val)]
print new_lst

Result:['word', 'second', 'first', 'first']

You can also use more_itertools.flatten

from more_itertools import flatten
import ast

list1 = ["['word']", "['second', 'first']", "['first']"]
list(flatten(ast.literal_eval(item) for item in list1))

Output:

['word', 'second', 'first', 'first']

This is another solution:

import re
list1 = ["['word']", "['second', 'first']", "['first']"]
pattern = re.compile(r'\w+')
m = pattern.findall(str(list1))

Result:

['word', 'second', 'first', 'first']

In order to flatten everything use itertools.chain.from_iterable:

>>> import itertools
>>> import ast
>>> list1 = ["['word']", "['second', 'first']", "['first']"]
>>> list2 = list(itertools.chain.from_iterable(map(ast.literal_eval, list1)))
>>> list2
['word', 'second', 'first', 'first']
Related