2012-07-25 16 views
7

Powiedzmy mam następujące dwie listy krotekPython Najłatwiej Sum listy skrzyżowaniu Lista krotki

myList = [(1, 7), (3, 3), (5, 9)] 
otherList = [(2, 4), (3, 5), (5, 2), (7, 8)] 

returns => [(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)] 

Chciałbym zaprojektować operację scalania, które łączy się te dwie listy poprzez sprawdzenie wszelkich skrzyżowań na pierwszy element krotki, jeśli są przecięcia, dodaj drugie elementy każdej krotki (połącz oba). Po operacji chciałbym sortować w oparciu o pierwszy element.

Ja również opublikowania tego powodu myślę, że jest to dość powszechny problem, który ma oczywiste rozwiązanie, ale czuję, że może być bardzo pythonic rozwiązania tej kwestii;)

Odpowiedz

14

używać słownika dla wyniku:

result = {} 
for k, v in my_list + other_list: 
    result[k] = result.get(k, 0) + v 

Jeśli chcesz listę krotek, możesz ją uzyskać przez result.items(). Wynikowa lista będzie w dowolnej kolejności, ale oczywiście możesz ją posortować w razie potrzeby.

(. Zauważ, że przemianowany list do zgodnych z umowami typu Pythona)

+0

Naprawdę czyste. Ładne rozwiązanie! –

+0

Zauważ, że 'result.items()' zwróci obiekt 'dict_items' w Pythonie 3. Oczywiście zawsze możesz zrobić' list (result.items()) '. – kamek

1

Sposób korzystania itertools:

>>> myList = [(1, 7), (3, 3), (5, 9)] 
>>> otherList = [(2, 4), (3, 5), (5, 2), (7, 8)] 

>>> import itertools 
>>> merged = [] 
>>> for k, g in itertools.groupby(sorted(myList + otherList), lambda e: e[0]): 
... merged.append((k, sum(e[1] for e in g))) 
... 
>>> merged 
[(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)] 

Ten pierwszy Łączy dwie listy razem i sortuje je. itertools.groupby zwraca elementy listy scalonej pogrupowane według pierwszego elementu krotki, więc sumuje je i umieszcza na liście scalonej.

4

Zastosowanie defaultdict:

from collections import defaultdict 
results_dict = defaultdict(int) 
results_dict.update(my_list) 
for a, b in other_list: 
    results_dict[a] += b 
results = sorted(results_dict.items()) 

Uwaga: Przy sortowaniu sekwencje, sorted rodzaju autorem pierwszego elementu w sekwencji. Jeśli pierwsze elementy są takie same, to porównuje drugi element. Można dać sorted funkcję sortowania przez, używając argumentu key kluczowe:

results = sorted(results_dict.items(), key=lambda x: x[1]) #sort by the 2nd item 

lub

results = sorted(results_dict.items(), key=lambda x: abs(x[0])) #sort by absolute value 
+0

(+1) Nie wiem, jak przez cały ten czas nie zdawałem sobie sprawy, że 'dict.update' akceptuje sekwencję taką samą, jak konstruktor dict. Poza tym sortowanie na pierwszym elemencie krotki, zgodnie z żądaniem OP, jest trywialne ('results = sorted (results_dict.items())') – mgilson

+0

@mgilson: Dzięki! Brakowało mi części o posortowanym wyniku. Dokona edycji. –

+0

@mgilson: Podobno 'update()' akceptowanie iterables zostało wprowadzone w 2.4. –

0
>>> [(k, sum(v for x,v in myList + otherList if k == x)) for k in dict(myList + otherList).keys()] 
[(1, 7), (2, 4), (3, 8), (5, 11), (7, 8)] 
>>> 

testowane zarówno Python2.7 i 3,2
dict(myList + otherList).keys() zwracającą iterowalny zawierającej zestaw kluczy dla połączonych list
sum(...) przejmuje pętlę "k" przez listę połączoną i dodaje elementy krotki "v" gdzie k == x

... ale dodatkowa pętla dodaje narzut przetwarzania. Użycie jawnego słownika zaproponowanego przez Sven Marnach pozwala tego uniknąć.