2009-11-22 14 views
9

Co muszę zrobić, to przekształcić coś takiegoPodział słownika list na listę słowników

{'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

w

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}] 

długości list wartości mogą się różnić! Jaki jest najszybszy sposób to zrobić (najlepiej bez pętli)?

+0

Możesz wyjaśnić swoje pytanie trochę ... Zajęło mi trochę czasu, aby zrozumieć, co chcesz ze swoim przykładzie: przekonwertować mapę z dwoma kluczami (klucz1 , key2) każdy z listą wartości (tej samej, ale różnej długości) do listy par, a para w pozycji i, klawisz1 i klucz2 jest ustawiony na ith element ich odpowiedniej listy. Czy to to? –

+0

Dlaczego fascynacja robieniem tego bez pętli? To głupie ograniczenie. – jcdyer

+0

to nie było konieczne, napisałem "najlepiej". Myślałem, że może to być szybki pythonic sposób to zrobić (niektóre funkcje magii jeszcze nie wiedziałem;)) –

Odpowiedz

11

działa dla dowolnej liczby kluczy

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 

Na przykład :

d = {'key3': [7, 8, 9], 'key2': [4, 5, 6], 'key1': [1, 2, 3]} 

>>> map(dict, zip(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key3': 7, 'key2': 4, 'key1': 1}, {'key3': 8, 'key2': 5, 'key1': 2}, {'key3': 9, 'key2': 6, 'key1': 3}] 

ogólne rozwiązanie, które działa na dowolnej liczbie wartości lub klawiszy: (python2.6)

>>> from itertools import izip_longest 
>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]} 
>>> map(lambda a: dict(filter(None, a)), izip_longest(*[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}] 

A jeśli nie masz python2.6:

>>> d = {'key2': [3, 4, 5, 6], 'key1': [1, 2]} 
>>> map(lambda a: dict(filter(None, a)), map(None, *[[(k, v) for v in value] for k, value in d.items()])) 
[{'key2': 3, 'key1': 1}, {'key2': 4, 'key1': 2}, {'key2': 5}, {'key2': 6}] 
2

Jeśli nie zawsze są dwa klucze można użyć:

[{'key1':a, 'key2':b} for (a,b) in zip(d['key1'], d['key2'])] 
1
>>> a = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 
>>> [dict((key, a[key][i]) for key in a.keys()) for i in range(len(a.values()[0]))] 
[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
+0

długość listy wartości może się różnić . zasięg (3) jest dość stały. – zlack

1
d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

keys = d.keys() 
vals = zip(*[d[k] for k in keys]) 
l = [dict(zip(keys, v)) for v in vals] 
print l 

produkuje

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
5

Zakładając liczbę klawiszy, a wartości na klucz, są zarówno arbitralne i a priori nieznany, najprościej jest uzyskać wynik z pętlami, oczywiście:

itit = thedict.iteritems() 
    k, vs = next(itit) 
    result = [{k: v} for v in vs] 
    for k, vs in itit: 
    for d, v in itertools.izip(result, vs): 
     d[k] = v 

Może być zwinięty, ale mam wątpliwości co do tego, jakie są tego konsekwencje (jeśli struktury danych są tak duże, że uzasadniają optymalizację wydajności, budowanie dodatkowej dodatkowej pamięci w pamięci poza to, co jest ściśle wymagane, może się okazać kosztowne - to moje proste podejście jest szczególnie ostrożne, aby uniknąć takich pośrednich struktur).

Edit: inna alternatywa, szczególnie interesujące, jeśli ogólne struktury danych są ogromne, ale w niektórych przypadkach zastosowania może trzeba tylko „kawałki” z „przekształconego” strukturze, jest budowanie klasy, która udostępnia interfejs czego potrzebujesz, ale robi to "w locie", a nie w "wielkim wybuchu", "raz na zawsze" (może to być szczególnie pomocne, jeśli oryginalna struktura może się zmienić, a przekształcona musi odzwierciedlać obecny stan oryginału, itp. itp.).

Oczywiście, w tym celu bardzo pomocne jest dokładne określenie, jakie cechy "listy słowników" będzie wykorzystywać twój kod źródłowy. Załóżmy na przykład, że wszystko, czego potrzebujesz, to indeksowanie "tylko do odczytu" (niezmiennianie, iterowanie, dzielenie, sortowanie, ...): X[x] musi zwracać słownik, w którym każdy klawisz k mapuje wartość, która (kalingowanie O oryginalny słownik list) X[x][k] is O[k][x]. Następnie:

class Wrap1(object): 
    def __init__(self, O): 
    self.O = O 
    def __getitem__(self, x): 
    return dict((k, vs[x]) for k, vs in self.O.iteritems()) 

Jeśli nie w rzeczywistości potrzebują owinięte strukturę do śledzenia zmian do pierwotnego, a następnie __getitem__ może dobrze także „cache” dict to powrót:

class Wrap2(object): 
    def __init__(self, O): 
    self.O = O 
    self.cache = {} 
    def __getitem__(self, x): 
    r = self.cache.get(x) 
    if r is None: 
     r = self.cache[x] = dict((k, vs[x]) for k, vs in self.O.iteritems()) 
    return r 

Należy pamiętać, że takie podejście może zakończyć się pewnym duplikowaniem w pamięci podręcznej, np. jeśli listy O mają po 7 pozycji, pamięć podręczna pod numerami x==6 i x==-1 może zakończyć się dwoma równorzędnymi dyktami; jeśli to jest problem, możesz na przykład normalizować negatywne x s w __getitem__, dodając do nich len(self.O), zanim przejdziesz dalej.

Jeśli potrzebujesz również iteracji, a także tego prostego indeksowania, to nie jest zbyt trudne: wystarczy dodać metodę __iter__, łatwo zaimplementowaną np. jako prosty generator ...:

def __iter__(self, x): 
    for i in xrange(len(self.O)): 
     yield self[i] 

I tak dalej, stopniowo, jeśli i tak trzeba coraz więcej funkcjonalności listę za (w najgorszym przypadku, gdy wdrożyły ten __iter__, można zbudować self.L = list(self) - powrót do „wielkiego wybuchu” podejście - i, dla każdego kolejnego żądania, punt do self.L ... ale będziesz musiał zrobić specjalną metaclass, jeśli chcesz wziąć to podejście również dla metod specjalnych lub użyć jakiejś subtelniejszej sztuczki, takiej jak self.__class__ = list; self[:] = self.L przez odpowiednie del s ;-).

1
list(map(dict, zip(*([(key, val) for val in data[key]] for key in data.keys())))) 
1

Bez pętli, proces wewnętrzny mapie jest iteracja w rzeczywistości, po prostu bez słowa kluczowego for

>>> x={'key1': [1, 2, 3], 'key2': [4, 5, 6]} 

>>> map(lambda x,y:{'key1':x,'key2':y},x['key1'],x['key2']) 

[{'key2': 4, 'key1': 1}, {'key2': 5, 'key1': 2}, {'key2': 6, 'key1': 3}] 
0

Co z?

d = {'key1': [1, 2, 3], 'key2': [4, 5, 6]} 
[dict(zip(d.keys(),i)) for i in zip(*d.values())] 

Powroty:

[{'key1': 1, 'key2': 4}, {'key1': 2, 'key2': 5}, {'key1': 3, 'key2': 6}]