2013-07-25 23 views
7

Podobne pytania zadawano wcześniej, ale rozwiązania tych nie działają dla mojego przypadku użycia (np. Making a flat list out of list of lists in Python i Flattening a shallow list in Python. Mam listę ciągów . i list, w którym lista osadzone mogą zawierać także ciągi i listy Chcę przekształcić prostą listę ciągów bez strun do przecinania do listy znakówSpłaszcz listę list i list ciągów i list w Pythonie

import itertools 

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

wynikowej liście.

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

Oczekiwane wynik:

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

Jaki jest najlepszy (Pythonic) sposób na zrobienie tego?

+0

Patrz: http://stackoverflow.com/questions/16176742/python-3-replacement-for-deprecated-compiler-ast-flatten-function –

+0

Zgadzam się, że jest prawie duplikatem http://stackoverflow.com/questions/5286541/how-can-i-flatten-lists-without-splitting-strings. Jedynym wymiarem, którego brakuje w tym pytaniu (którego nie znalazłem przed zadaniem mi pytania) jest kwestia arbitralnych poziomów zagnieżdżania. Jednak zamieszczone tam rozwiązanie (i na http: // stackoverflow.com/questions/16176742/python-3-replacement-for-depiler-compiler-ast-flatten-function) radzi sobie z tym problemem całkiem ładnie, przynajmniej w przypadku, który podałem. –

+0

OP: użyj 'basestring' zamiast' str', aby nie dzielić 'unicode'. – 2rs2ts

Odpowiedz

4

następujące utwory na smyczki (i będzie łatwo dostosować do innych typów):

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'] 
+2

To ma trochę nadmiarowości - isinstance uwzględnia dziedziczenie i możesz przełączyć test none na gałąź rekurencyjną – Marcin

+0

@marcin Dzięki za komentarz. Poprawiłem swój kod, aby rozwiązać te problemy. –

1

W jednym specjalizuje się przypadek, gdy żaden z elementów listy zawiera jeden z następujących ograniczników []', można użyć następujących włamać się. Nie profilowałem tego, ale wygląda na oczywiste, że miałoby to lepszą wydajność niż oczywiste i czystsze rozwiązanie rekursywne.

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

Zgadzam się, to jest brudny hack, ale wykonuje pracę, bez większego wysiłku.

+0

Zgadzam się z tym, z wyjątkiem tego, że jasno wyjaśniłeś jego wady. Myślę, że to dobre odniesienie. – 2rs2ts

1

Jest to ogólny rekurencyjne spłaszczyć, które mogą być używane do pracy z dowolnej kombinacji typów, które powinny lub nie powinny być spłaszczone:

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 i chain mogłyby zostać użyte do wygenerowania generycznej wersji do iteracyjnej opartej .

9

Często powtarzaną funkcję flatten można zastosować w tej sytuacji, wprowadzając prostą modyfikację.

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 będzie upewnić się, że oba str i unicode obiekty nie są podzielone.

Dostępne są także wersje, które nie mają atrybutu i mają atrybut __iter__. Nie wiem o tym wszystkim, ponieważ myślę, że teraz str ma teraz ten atrybut. Ale warto o tym wspomnieć.

(proszę upvote połączoną odpowiedź.)

+1

Podejrzewam, że lepiej byłoby po prostu połączyć się z tą odpowiedzią, w ten sposób byłaby to wskazówka i nie wprowadziłaby duplikacji. –

2

Korzystanie rekursji.

def flattern(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']