2015-06-03 28 views
5

Czy istnieje skuteczny sposób scalania dwóch list krotek w python, na podstawie wspólnej wartości. Obecnie robię co następuje:Przyłącz listy według wartości

name = [ 
     (9, "John", "Smith"), 
     (11, "Bob", "Dobbs"), 
     (14, "Joe", "Bloggs") 
     ] 

occupation = [ 
       (9, "Builder"), 
       (11, "Baker"), 
       (14, "Candlestick Maker") 
       ] 

name_and_job = [] 

for n in name: 
    for o in occupation: 
     if n[0] == o[0]: 
      name_and_job.append((n[0], n[1], n[2], o[1])) 


print(name_and_job) 

powraca:

[(9, 'John', 'Smith', 'Builder'), (11, 'Bob', 'Dobbs', 'Baker'), (14, 'Joe', 'Bloggs', 'Candlestick Maker')] 

Podczas gdy ten kod działa perfekcyjnie dla małych listach, to niezwykle wolno przez dłuższy list z milionami rekordów. Czy istnieje skuteczniejszy sposób, aby to napisać?

EDYCJA Numery w pierwszej kolumnie są niepowtarzalne.

EDYTOWANIE Zmodyfikowano kod QR dla Jana Kugelmana. Dodano get(), na wypadek gdyby słownik nazw nie zawierał pasującego klucza w słowniku zawodów:

>>>> names_and_jobs = {id: names[id] + (jobs.get(id),) for id in names} 
>>>> print(names_and_jobs) 
{9: ('John', 'Smith', None), 11: ('Bob', 'Dobbs', 'Baker'), 14: ('Joe', 'Bloggs', 'Candlestick Maker')} 
+3

Dla wydajności często prawo struktura danych jest kluczem. Użyj słownika. –

+2

Zostawić usuniętą odpowiedź jako komentarz dla <10 tysięcy użytkowników. * Jeśli * twoja lista jest pewna, że ​​ma * dokładnie * pasujące wpisy, możesz użyć ['zip'] (https://docs.python.org/2/library/functions.html#zip) wraz z [listą ze zrozumieniem] (https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions) jak w '[(x [0], x [1], x [2], y [1]) dla x, y in zip (nazwa, zawód)] ' –

+0

@BhargavRao Myślę, że to uczciwe założenie, dlaczego inaczej OP nie pokazywałby niezgodności w przykładzie? –

Odpowiedz

5

Używaj słowników zamiast płaskich list.

names = { 
    9: ("John", "Smith"), 
    11: ("Bob", "Dobbs"), 
    14: ("Joe", "Bloggs") 
} 

jobs = { 
    9: "Builder", 
    11: "Baker", 
    14: "Candlestick Maker" 
} 

Jeśli trzeba konwertować je do tego formatu, można zrobić:

>>> {id: (first, last) for id, first, last in name} 
{9: ('John', 'Smith'), 11: ('Bob', 'Dobbs'), 14: ('Joe', 'Bloggs')} 
>>> {id: job for id, job in occupation} 
{9: 'Builder', 11: 'Baker', 14: 'Candlestick Maker'} 

Byłoby wtedy być bułka z masłem, aby połączyć dwa.

names_and_jobs = {id: names[id] + (jobs[id],) for id in names} 
+1

Zakłada się, że wartości 9, 11 i 14 są unikalne. Co, jeśli to tylko "lata doświadczenia", a nie wyjątkowe w ogóle? –

+4

@TravisGriggs Kod OP traktuje je jak identyfikatory. Zakładam, że to jest intencja. –

+4

@TravisGriggs tak, ale łączy się tylko sprawdzając równość dla tej liczby, więc możesz założyć, że są unikatowe –

1
from collections import OrderedDict 
from itertools import chain 

od = OrderedDict() 


for ele in chain(name,occupation): 
    od.setdefault(ele[0], []).extend(ele[1:]) 


print([[k]+val for k,val in od.items()]) 

[[9, 'John', 'Smith', 'Builder'], [11, 'Bob', 'Dobbs', 'Baker'], [14, 'Joe', 'Bloggs', 'Candlestick Maker']] 

Jeśli chcesz dane zamówionego przez jaki pojawia się w nazwach to trzeba użyć OrderedDict jako normalne dicts są nieuporządkowane.

Można również dodać dane w pętli tworzenia pożądanych krotki wtedy właśnie dzwoni od.values ​​aby uzyskać listę krotek:

from collections import OrderedDict 
from itertools import chain 

od = OrderedDict() 

for ele in chain(name, occupation): 
    k = ele[0] 
    if k in od: 
     od[k] = od[k] + ele[1:] 
    else: 
     od[k] = ele 

print(od.values()) 
[(9, 'John', 'Smith', 'Builder'), (11, 'Bob', 'Dobbs', 'Baker'), (14, 'Joe', 'Bloggs', 'Candlestick Maker')] 
+0

err * OderederDict *? Za dużo whisky? –

+0

@BhargavRao Kod OP to iteracja po nazwach w kolejności, użycie normalnego polecenia spowoduje, że dane nie będą miały żadnego zamówienia, jednak nazwy pojawią się w nazwach odpowiadających zamówieniu –

+0

Ey! Nie zrozumiałeś mojego punktu;) Teraz masz mój głos :) –