Smooth list of strings and lists of strings and lists in Python

Similar questions were asked before, but solutions for them do not work for my use case (for example, Creating a flat list from a list of lists in Python and Smoothing a small list in Python . I have a list of strings and lists where the built-in list can also contain strings and lists I want to turn this into a simple list of strings without splitting the strings into a list of characters.

import itertools list_of_menuitems = ['image10', ['image00', 'image01'], ['image02', ['image03', 'image04']]] chain = itertools.chain(*list_of_menuitems) 

Summary list:

 ['i', 'm', 'a', 'g', 'e', '1', '0', 'image00', 'image01', 'image02', ['image03', 'image04']] 

Expected Result:

 ['image10', 'image00', 'image01', 'image02', 'image03', 'image04'] 

What is the best (Pythonic) way to do this?

+10
python list
source share
5 answers

The frequently repeated flatten function can be applied to this circumstance with a simple modification.

 from collections import Iterable def flatten(coll): for i in coll: if isinstance(i, Iterable) and not isinstance(i, basestring): for subc in flatten(i): yield subc else: yield i 

basestring will ensure that str and unicode objects are not shared.

There are also versions that are calculated on i that do not have the __iter__ attribute. I don't know about all this because I think str now has this attribute. But worth mentioning.

(Please support the related answer.)

+11
source share

Using recursion.

 def flatten(A): rt = [] for i in A: if isinstance(i,list): rt.extend(flattern(i)) else: rt.append(i) return rt 

Test:

 >>> list_of_menuitems = ['image10', ['image00', 'image01'], ['image02', ['image0 3', 'image04']]] >>> flattern(list_of_menuitems) ['image10', 'image00', 'image01', 'image02', 'image03', 'image04'] 
+5
source share

The following lines are for strings (and will be easily adapted to other types):

 def flatten_to_strings(listOfLists): """Flatten a list of (lists of (lists of strings)) for any level of nesting""" result = [] for i in listOfLists: # Only append if i is a basestring (superclass of string) if isinstance(i, basestring): result.append(i) # Otherwise call this function recursively else: result.extend(flatten_to_strings(i)) return result flatten_to_strings(list_of_menuitems) Out[2]: ['image10', 'image00', 'image01', 'image02', 'image03', 'image04'] 
+4
source share

In one specialized case, when none of the items in the list contains one of the following delimiters []' , you can use the following hack. I did not profile it, but it is obvious that this will have better performance than an obvious and cleaner recursive solution.

 >>> str(list_of_menuitems).translate(None,"[]'").split(',') ['image10', ' image00', ' image01', ' image02', ' image03', ' image04'] 

I agree, this is a dirty hack, but it does the JOB effortlessly.

+2
source share

This is a common recursive splice that can be used to work with any combination of types that should or should not be flattened:

 import collections def generic_flatten(seq, flatten_types=(tuple,list,set),atom_types=(basestring,dict),fixtype=True): newseq = [] for item in seq: if (not isinstance(collections.Iterable)) or any(isinstance(i,t) for t in atom_types): newseq.append(item) elif any(isinstance(i,t) for t in flatten_types): # set flatten_types to (object,) or (collections.Iterable,) to disable check newseq.extend(generic_flatten(item, flatten_types, atom_types,fixtype) if fixtype and type(newseq) is not type(seq): newseq = type(seq)(newseq) return newseq 

yield and chain can be used to create a generic version based on an iterator.

+1
source share

All Articles