Ich habe eine Liste mit Wörterbüchern (List[Dict, Dict, ...]). Ich möchte die Liste anhand von zwei Schlüsseln vereinheitlichen, möchte aber den Wert eines anderen Schlüssels im Wörterbuch beibehalten, um sicherzustellen, dass ich ihn nicht durch Erstellen verliere eine Liste in dem Schlüssel, den ich behalten möchte. Ich benutze Python für den Code. Wenn es von Bedeutung ist, Python 3.x, um genau zu sein.

Nehmen wir an, ich habe die folgende Liste von Wörterbüchern mit drei Schlüsseln: number, favorite und color. Ich möchte die Listenelemente mit den Schlüsseln number und favorite vereinheitlichen. Für die Wörterbücher mit denselben Werten number und favorite möchte ich jedoch eine Liste unter dem Schlüssel color hinzufügen, um sicherzustellen, dass ich alle color habe für die gleiche Kombination von number und favorite. Diese Liste sollte auch eindeutig sein, da die wiederholten color für dieselbe Kombination nicht benötigt werden. Wenn das Endergebnis jedoch nur ein Element für die Schlüsselfarbe enthält, sollte es sich um eine Zeichenfolge und nicht um eine Liste handeln.

lst = [
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': False, 'color': 'green'},
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': True, 'color': 'red'},
{'number': 2, 'favorite': False, 'color': 'red'}]

Mit dem oben genannten Uniqify würde ich folgendes Ergebnis erhalten:

lst = [
    {'number': 1, 'favorite': False, 'color': {'red', 'green'}},
    {'number': 1, 'favorite': True, 'color': 'red'},
    {'number': 2, 'favorite': False, 'color': 'red'},
]

Beachten Sie, dass es nur eine Instanz von red gibt, in der number 1 und favorite False ist, obwohl sie zweimal in der Liste enthalten war, bevor sie eindeutig war . Beachten Sie auch, dass wenn es im zweiten Diktat nur ein Element für den Schlüssel color gibt, es sich um eine Zeichenfolge und nicht um eine Liste handelt.

20
KaanTheGuru 18 Jän. 2019 im 09:45

6 Antworten

Beste Antwort

Mit reinem Python können Sie in ein OrderedDict einfügen, um die Einfügereihenfolge beizubehalten:

from collections import OrderedDict

d = OrderedDict()
for l in lst:
    d.setdefault((l['number'], l['favorite']), set()).add(l['color'])

[{'number': k[0], 'favorite': k[1], 'color': v.pop() if len(v) == 1 else v} 
    for k, v in d.items()]   
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]

Dies kann auch ganz einfach mit der Pandas GroupBy API erfolgen:

import pandas as pd

d = (pd.DataFrame(lst)
       .groupby(['number', 'favorite'])
       .color
       .agg(set)
       .reset_index()
       .to_dict('r'))
d
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': {'red'}, 'favorite': True, 'number': 1},
#  {'color': {'red'}, 'favorite': False, 'number': 2}]

Wenn die Bedingung einer Zeichenfolge für ein einzelnes Element erforderlich ist, können Sie verwenden

[{'color': (lambda v: v.pop() if len(v) == 1 else v)(d_.pop('color')), **d_} 
     for d_ in d]
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]
15
cs95 18 Jän. 2019 im 07:10

Eine Lösung in reinem Python wäre die Verwendung eines defaultdict mit einem zusammengesetzten Schlüssel. Damit können Sie Ihre Werte zusammenführen. Anschließend können Sie aus diesem Wörterbuch erneut eine Liste erstellen.

from collections import defaultdict

dct = defaultdict([])

for entry in lst:
    dct[(entry['number'], entry['favorite'])].append(entry['color'])

lst = [{'number': key[0], 'favorite': key[1], color: value if len(value) > 1 else value[0]}
    for key, value in dct.items()]
3
Praind 18 Jän. 2019 im 07:14

Oder groupby von itertools:

import itertools
lst = [
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': False, 'color': 'green'},
{'number': 1, 'favorite': False, 'color': 'red'},
{'number': 1, 'favorite': True, 'color': 'red'},
{'number': 2, 'favorite': False, 'color': 'red'}]
l=[list(y) for x,y in itertools.groupby(sorted(lst,key=lambda x: (x['number'],x['favorite'])),lambda x: (x['number'],x['favorite']))]
print([{k:(v if k!='color' else list(set([x['color'] for x in i]))) for k,v in i[0].items()} for i in l])

Ausgabe:

[{'number': 1, 'favorite': False, 'color': ['green', 'red']}, {'number': 1, 'favorite': True, 'color': ['red']}, {'number': 2, 'favorite': False, 'color': ['red']}]
2
U10-Forward - Reinstate Monica 18 Jän. 2019 im 08:02

Sie können ein geordnetes Wörterbuch mit Standardwerten von set verwenden. 1 Wiederholen Sie anschließend Ihre Wörterbuchliste mit (number, favorite) als Schlüssel. Dies funktioniert, da Tupel hashbar sind und daher als Wörterbuchschlüssel verwendet werden dürfen.

Es wird empfohlen, eine konsistente Struktur zu verwenden. Verwenden Sie also anstelle von Zeichenfolgen für einzelne Werte und Mengen für mehrere Werte durchgehend Mengen:

from collections import OrderedDict, defaultdict

class DefaultOrderedDict(OrderedDict):
    def __missing__(self, k):
        self[k] = set()
        return self[k]

d = DefaultOrderedDict()  # Python 3.7+: d = defaultdict(set)

for i in lst:
    d[(i['number'], i['favorite'])].add(i['color'])

res = [{'number': num, 'favorite': fav, 'color': col} for (num, fav), col in d.items()]

print(res)
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': {'red'}, 'favorite': True, 'number': 1},
#  {'color': {'red'}, 'favorite': False, 'number': 2}]

Wenn Sie darauf bestehen, je nach Anzahl der Farben unterschiedliche Typen zu haben, können Sie das Listenverständnis neu definieren, um eine ternäre Anweisung zu verwenden:

res = [{'number': num, 'favorite': fav, 'color': next(iter(col)) if len(col) == 1 else col} \
       for (num, fav), col in d.items()]

print(res)
# [{'color': {'green', 'red'}, 'favorite': False, 'number': 1},
#  {'color': 'red', 'favorite': True, 'number': 1},
#  {'color': 'red', 'favorite': False, 'number': 2}]

1 Der Punkt ist in Python-Versionen vor 3.7 bemerkenswert, in denen die Reihenfolge der Einfügung von Wörterbüchern nicht garantiert ist. Mit Python 3.7+ können Sie die Einfügereihenfolge nutzen und einfach dict oder eine Unterklasse von dict wie collections.defaultdict verwenden.

1
jpp 18 Jän. 2019 im 15:25

Hier ist eine Möglichkeit, dies zu tun:

Ich habe zuerst ein dict mit einem Tupel als zusammengesetzten Schlüssel erstellt und dann aus diesem dict eine neue Liste erstellt. Sie können Verständnis schreiben, um Linien weiter zu reduzieren und zu optimieren. Ich hoffe, es hilft.

new_dict = {}

for item in lst:
    try: # if already exists then append to the list
        new_dict.get((item['number'], item['favorite']))
        new_dict[(item['number'], item['favorite'])].append(item['color'])
    except KeyError: # if it doesn't then create a new entry to that key
        new_dict[(item['number'], item['favorite'])] = [item['color']]


final_list = []
for k, v in new_dict.items(): # keep appending dicts to our list
    final_list.append({'number': k[0], 'favorite': k[1], 'color':set(v)})

print(final_list)

Ausgänge:

[{'number': 1, 'favorite': False, 'color': {'green', 'red'}}, {'number': 1, 'favorite': True, 'color': {'red'}}, {'number': 2, 'favorite': False, 'color': {'red'}}]
0
Vineeth Sai 18 Jän. 2019 im 07:09

Ein Freund von mir hat die folgende Funktion ausgeführt, um dieses Problem zu lösen, ohne externe Bibliotheken zu verwenden:

def uniqifyColors(l):
    for elem in l:
        for item in l:
            if elem['number'] == item['number'] and elem['favorite'] == item['favorite']:
                for clr in item['color']:
                    if clr not in elem['color']:
                        elem['color'].append(clr)
    return l

Nachdem er diese Python-Funktion verwendet hatte, führte er einfach eine einfache Uniqifizierung durch, um die eindeutigen Ergebnisse aus der Liste zu erhalten. Es wird jedoch keine einzelne Farbe als Zeichenfolge beibehalten, sondern eine Liste mit einem einzelnen Element.

0
KaanTheGuru 22 Jän. 2019 im 16:00