Szukałem sporo, ale nie znalazłem podobnego pytania.

Mam dwie listy słowników w następującym formacie:

data1 = [
    {'id': 4, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
    {'id': 4, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
    {'id': 6, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
    {'id': 7, 'date_time': datetime.datetime(2020, 4, 3, 16, 14, 21)},
]

data2 = [
    {'id': 4, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
    {'id': 6, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
]

żądane wyjście:

final_data = [
    {'id': 4, 'date_time': datetime.datetime(2020, 4, 3, 12, 34, 40)},
    {'id': 7, 'date_time': datetime.datetime(2020, 4, 3, 16, 14, 21)},
]

Chcę tylko słowników, które są w data1, a nie w data2.

Do tej pory, kiedy znalazłem dopasowanie w dwóch pętlach for, wyrzuciłem słownik z listy, ale nie wydaje mi się to dobre podejście. Jak mogę osiągnąć pożądaną wydajność?

Nie musi to być oszczędne czasowo, ponieważ na każdej liście będzie maksymalnie kilkadziesiąt słowników


counter_i = 0

for i in range(len(data1)):
    counter_j = 0
    for j in range(len(data2)):
        if data1[i-counter_i]['id'] == data2[j-counter_j]['id'] and data1[i-counter_i]['date_time'] == data2[j-counter_j]['date_time']
            data1.pop(i-counter_i)
            data2.pop(j-counter_j)
            counter_i += 1 
            counter_j += 1 
            break
0
popcorn 3 kwiecień 2020, 18:17

3 odpowiedzi

Najlepsza odpowiedź

Jeśli wydajność nie jest problemem, dlaczego nie:

for d in data2:
    try:
        data1.remove(d)
    except ValueError:
        pass  

list.remove sprawdza równość obiektów, a nie tożsamość, więc będzie działać dla dykt z równymi kluczami i wartościami. Ponadto list.remove usuwa tylko jedno wystąpienie naraz.

2
schwobaseggl 3 kwiecień 2020, 15:40

Możesz iść w dowolny sposób:

Metoda 1:

#using filter and lambda function
final_data = filter(lambda i: i not in data2, data1) 
final_data = list(final_data)

Metoda 2:

# using list comprehension to perform task 
final_data = [i for i in data1 if i not in data2] 
-1
Avishek Das Ayan 3 kwiecień 2020, 15:30

Odpowiedź schwobaseggl jest prawdopodobnie najczystszym rozwiązaniem (po prostu zrób kopię przed usunięciem, jeśli chcesz zachować dane1 w stanie nienaruszonym).

Ale jeśli chcesz użyć różnicy zestawów ... cóż, dykty nie mogą być hashowane, ponieważ ich podstawowe dane mogą się zmienić i prowadzić do problemów (ten sam powód, dla którego listy lub zestawy również nie mogą być hashowane).

Możesz jednak pobrać wszystkie pary dyktowania w zestawie zamrożonym, aby reprezentowały słownik (zakładając, że wartości słownika są hashable -schwobaseggl). Mrożonki można mieszać, więc możesz dodać je do zestawu, aby uzyskać normalną różnicę między ustawieniami. I na koniec zrekonstruuj słowniki: D.

Właściwie nie polecam tego robić, ale zaczynamy:

final_data = [
  dict(s)
  for s in set(
    frozenset(d.items()) for d in data1
  ).difference(
    frozenset(d.items()) for d in data2
  )
]
1
Faboor 3 kwiecień 2020, 15:46