2014-04-29 10 views
8

Wie kann ich uniquify die folgende Liste in Python:Get Liste der einzigartigen Multi-Sets

all_the_ways = [(5,), (2, 2, 1), (2, 1, 2), (2, 1, 1, 1), (1, 2, 2),\ 
       (1, 2, 1, 1), (1, 1, 2, 1), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 

Wunsch Ausgabe lautet:

[(5,), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)] 

dh ich von Tupeln loswerden müssen, die die gleiche haben Satz von Zahlen, aber in anderer Reihenfolge.

Ich versuchte

set(all_the_ways) 

aber nur Elemente umzusetzen.

Und wenn ich

list(map(set, all_the_ways)) 

die Dinge tun, immer nur noch schlimmer:

[{5}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1}] 

Mit anderen Worten: Ich brauche innere Tupel zu einer Sammlung zu konvertieren, die mehrere gleiche Elemente erlaubt (set ist nicht geeignet) und für die Permutationen von Elementen nicht die Sammlung selbst ändern (ähnlich wie C++ multiset)

+0

Was sollte die Ausgabe, wenn 'all_the_ways = [(2, 1, 2), (2, 2, 1)]' sein? – thefourtheye

+0

erstes oder zweites Tupel, ist egal – tsionyx

+0

Also sollte das Ergebnis in 'all_the_ways' sein? – thefourtheye

Antwort

5

Wie wäre es damit:

list(set(tuple(sorted(s)) for s in all_the_ways)) 

Ausgang:

[(1, 2, 2), (5,), (1, 1, 1, 1, 1), (1, 1, 1, 2)] 

Es wird allerdings die Reihenfolge der einzelnen Tupel mangle. Ich gehe davon aus, dass das egal ist, da Tupel, die denselben Zahlensatz enthalten, in Ihrem Fall als gleich angesehen werden. Was dies bedeutet, ist, dass am Ende könnte die Ausgabeliste Tupel enthält, die nicht unter dem ursprünglichen Eingang sind zum Beispiel (Kredit @thefourtheye):

all_the_ways = [(2, 1, 2), (2, 2, 1)] 
# Output: [(1, 2, 2)] 

Dies kann oder kann nicht ein Problem sein, und wenn es ist, Sie können die robusteren Lösungen verwenden, die bereits in den anderen ausgezeichneten Antworten erwähnt werden.

+1

Wenn die Kombination '(1, 2, 2)' nicht in 'all_the_ways' existiert, könnte dies ein Problem sein. Aber nicht sicher, ob es mit dem OP in Ordnung ist. Schon + 1ed – thefourtheye

+0

Das stimmt, wie ich in der Antwort erwähnt habe. Ich habe mich entschieden, das Problem der Bestellung nicht zu behandeln, um eine einfachere Perspektive zu bieten, nur für den Fall, dass es in diesem Problem keine Einschränkung gibt. + 1s zu allen auftragserhaltenden Lösungen! :) –

+1

Eigentlich geht es nicht um die Reihenfolge. Wenn 'all_the_ways = [(2, 1, 2), (2, 2, 1)]', wird die Ausgabe '[(1, 2, 2)]' 'sein, was nicht in' all_the_ways' steht. Dies könnte ein Problem sein, denke ich. – thefourtheye

0

Ich nehme an, dass Sie zwei Elemente "gleich" betrachten, wenn sie die gleichen Werte enthalten, unabhängig von der Reihenfolge.

So können Sie „canonicalize“ jedes Tupel durch das Sortieren, konvertieren zurück zu Tupeln (sie sind also hashable) und Duplikate entfernen set mit wieder:

set(tuple(sorted(tup)) for tup in all_the_ways) 

Sie können auch die original „äußere“ bewahren Bestellen Sie, indem Sie OrderedSet anstelle von set verwenden.

3

Verwenden collections.Counter() die einzigartigen Multimengen zu identifizieren:

>>> from collections import Counter 

>>> all_the_ways = [(5,), (2, 2, 1), (2, 1, 2), (2, 1, 1, 1), (1, 2, 2),\ 
       (1, 2, 1, 1), (1, 1, 2, 1), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 
>>> result = [] 
>>> seen = set() 
>>> for tup in all_the_ways: 
     key = tuple(sorted(Counter(tup).items())) # unique signature 
     if key not in seen: 
      result.append(tup) 
     seen.add(key) 

>>> result 
[(5,), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)] 
+0

Ich habe darüber nachgedacht dies nur, aber konnte nicht weiter gehen, da sie nicht waschbar sind ... :( – thefourtheye

+0

@thefourtheye Sobald das Zählen gemacht wird, macht eine Art der Artikel kanonische Bestellung, und tuplizing macht es hashable :-) –

+0

Warum nicht einfach ' Counter (Tupel (sortierte (i)) für i in all_the_ways) .keys() '? –

1

dies auch sein mag?:

result = {tuple(sorted(x)) for x in all_the_ways} 
2

Wenn die Reihenfolge keine Rolle spielt Sie diese

from collections import Counter 
>>> {frozenset(Counter(tup).items()):tup for tup in data}.values() 
# [(1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1), (5,)] 

verwenden können, wenn Sie die Reihenfolge,

from collections import Counter, OrderedDict 
OrderedDict([frozenset(Counter(tup).items()),tup] for tup in data).values() 
# [(5,), (1, 2, 2), (1, 1, 1, 2), (1, 1, 1, 1, 1)] 

In beiden Lösungen erhalten wollen wir auf frozenset verlassen, weil die Objekte set nicht hashbar sind, da sie veränderbar sind. Im ersten Fall konstruieren wir ein Wörterbuch mit der Häufigkeit der Zahlen (bestimmt mit Counter) als Schlüssel und dem aktuellen Tupel als dem entsprechenden Wert. Sobald die Wörterbuchkonstruktion abgeschlossen ist, nehmen wir alle Werte, die den Tupeln entsprechen, an.

Im zweiten Fall verwenden wir einfach OrderedDict, um die Bestellung aufrechtzuerhalten.

+1

+1 Für die nette Kombination von OrderedDict, Frozenset und Counter. –

+0

@RaymondHettinger Danke :-) – thefourtheye

1

Versuchen

from collections import OrderedDict 
print OrderedDict.fromkeys(map(lambda x: tuple(sorted(x)), all_the_ways)).keys() 

oder

print set(map(lambda x: tuple(sorted(x)), all_the_ways))