2010-01-12 6 views
27

Ich gehe durch eine ganze Reihe von Tupeln mit einer Viele-zu-Viele-Korrelation, und ich möchte ein Wörterbuch erstellen, in dem jedes b von (a, b) eine Liste aller a enthält, die einem b entsprechen. Es scheint peinlich zu sein, nach einer Liste im Schlüssel b des Wörterbuchs zu suchen, dann nach einem a zu suchen und dann a, wenn es nicht schon da ist, jedes Mal durch die Tupel-Digest-Schleife anzuhängen; Aber ich habe noch keinen besseren Weg gefunden. Gibt es einen? Gibt es eine andere Möglichkeit, dies zu tun, die viel hübscher ist?Effiziente Möglichkeit, entweder eine Liste zu erstellen oder an sie anzuhängen, wenn eine bereits existiert?

+1

verwenden, indem schönere Sie syntaktisch bedeuten oder algorithmisch? –

Antwort

36

Siehe the docs für die setdefault() Methode:

setdefault (Taste [, default])
Wenn Schlüssel im Wörterbuch enthalten ist, dessen Wert zurück. Wenn nicht, Schlüssel mit einem Wert von Standard einfügen und Standard zurückgeben. Standard Standardwert ist Keine.

Sie können dies als einen einzigen Aufruf verwenden, b erhalten, wenn es vorhanden ist, oder setzen Sie b auf eine leere Liste, wenn es nicht bereits vorhanden ist - und so oder so, Rückkehr b:

>>> key = 'b' 
>>> val = 'a' 
>>> print d 
{} 
>>> d.setdefault(key, []).append(val) 
>>> print d 
{'b': ['a']} 
>>> d.setdefault(key, []).append('zee') 
>>> print d 
{'b': ['a', 'zee']} 

das Kombinieren mit einem einfachen „nicht in“ zu überprüfen und Sie haben getan, was Sie nach in drei Zeilen sind:

>>> b = d.setdefault('b', []) 
>>> if val not in b: 
... b.append(val) 
... 
>>> print d 
{'b': ['a', 'zee', 'c']} 
+3

'defaultdict' ist ein bisschen schöner als' setdefault', vorausgesetzt, Sie haben Python 2.5 oder höher. – ephemient

+1

Ich bin mit 2,34 fest, also ist dies eigentlich die Antwort, für mich - danke, James! – user249228

+5

D'oh. 'set()' ist nett, aber ist erst 2.4 eingebaut. Warum ist dein Python so alt? :-( – ephemient

2

können Sie Ihre Tupel O sortieren (n log n), dann erstellen Sie Ihre Wörterbuch O (n)

oder simplier O (n) könnte aber schwere Last auf das Gedächtnis bei vielen Tupeln verhängen:

your_dict = {} 
for (a,b) in your_list: 
    if b in your_dict: 
     your_dict[b].append(a) 
    else: 
     your_dict[b]=[a] 

Hmm es ist so ziemlich das gleiche wie Sie beschrieben haben. Was ist daran unangenehm?

Sie könnten auch eine SQL-Datenbank verwenden, um die Drecksarbeit zu erledigen.

+0

Die einfachere Methode ist übrigens O (n), also ist es besser, die Tupelmethode zu sortieren. – kennytm

+0

ja, das habe ich auch in bearbeiteter Version gesagt. –

+0

Kommentare zu Downvoting? –

0

Ich bin nicht sicher, wie Sie aus dem Schlüssel-Test zu bekommen, aber sobald sie Schlüssel/Wert-Paar initialisiert wurde es einfach :)

d = {} 
if 'b' not in d: 
    d['b'] = set() 
d['b'].add('a') 

Das Set wird sichergestellt, dass nur 1 von ‚einem "ist in der Sammlung. Sie müssen jedoch die erste 'b' Prüfung durchführen, um sicherzustellen, dass der Schlüssel/Wert existiert.

+0

neugierig warum -1? ist das irgendwie falsch? Ich werde die Antwort löschen, wenn es falsch ist. –

15

Sie nicht wirklich gebunden Listen Unter der Annahme, defaultdict und set sind recht praktisch.

import collections 
d = collections.defaultdict(set) 
for a, b in mappings: 
    d[b].add(a) 

Wenn Sie wirklich Listen wollen anstelle von Sätzen, könnten Sie diese folgen mit einem

for k, v in d.iteritems(): 
    d[k] = list(v) 

Und wenn Sie wirklich anstelle eines defaultdict ein dict möchten, können Sie sagen

d = dict(d) 

Ich sehe wirklich keinen Grund, den Sie wollen, obwohl.

+0

ah ja, das geht um die anfängliche Prüfung für keinen Wert. Danke! Ich lernte etwas neues. :) –

+1

+1 für 'defaultdict', weil es wirklich die Pythonic Lösung ist. – jathanism

+1

Ich mochte auch, wie [dieser Typ half mir mit defaultdict kommen (Lambda: defaultdict (Liste))] (http://ohuiginn.net/mt/2010/07/nested_dictionaries_in_python.html) – lkraav

4

Verwenden Sie Sammlungen.defaultdict

your_dict = defaultdict(list) 
for (a,b) in your_list: 
    your_dict[b].append(a) 
+0

Wollen Sie nicht verwenden anhängen? – interjay

+0

Ja, das habe ich so gemeint. Danke –

+0

OPs "dann anhängen, wenn es nicht schon da ist" lässt mich denken, dass die ursprüngliche Liste Duplikate haben könnte, die herausgefiltert werden sollten, weshalb ich 'set' anstatt' liste' verwendet habe. – ephemient

3

Statt eine if zu verwenden, AFAIK ist es pythonic stattdessen einen try Block zu verwenden.

your_list=[('a',1),('a',3),('b',1),('f',1),('a',2),('z',1)] 

your_dict={} 
for (a,b) in your_list: 
    try: 
     your_dict[b].append(a) 
    except KeyError: 
     your_dict[b]=[a] 

print your_dict 
0

Dict get methode? Es gibt den Wert von my_dict[some_key] wenn some_key im Wörterbuch enthalten ist, und wenn nicht - gibt einigen Standardwert ([] im Beispiel unten):

my_dict[some_key] = my_dict.get(some_key, []).append(something_else) 
0

Es gibt eine andere Art und Weise, die eher effizient ist (wenn auch vielleicht nicht so effizient wie Sets) und einfach. Es ähnelt in der Praxis defaultdict, erfordert jedoch keinen zusätzlichen Import. Wenn Sie ein Diktat mit leeren (None) Schlüsseln haben, bedeutet das, dass Sie die Diktatschlüssel auch irgendwo erstellen. Sie können dies mit der Methode dict.fromkeys tun, und diese Methode ermöglicht es auch, einen Standardwert für alle Schlüssel festzulegen.

keylist = ['key1', 'key2'] 
result = dict.fromkeys(keylist, []) 

wo result sein wird: { 'key1': [], 'key2': []}

Dann können Sie Ihre Schleife tun und result['key1'].append(..) direkt

Verwandte Themen