2012-12-28 15 views
10

Przyjaciół, mam listę słowników:Python. Manipulacja z listą słowników

my_list = 
[ 
{'oranges':'big','apples':'green'}, 
{'oranges':'big','apples':'green','bananas':'fresh'}, 
{'oranges':'big','apples':'red'}, 
{'oranges':'big','apples':'green','bananas':'rotten'} 
] 

Chcę utworzyć nową listę, gdzie częściowe kopie zostały wyeliminowane.

W moim przypadku ten słownik musi zostać wyeliminowana:

{'oranges':'big','apples':'green'} 

, ponieważ powiela dłuższe słowniki:

{'oranges':'big','apples':'green','bananas':'fresh'} 
{'oranges':'big','apples':'green','bananas':'rotten'} 

Stąd, pożądany rezultat:

[ 
{'oranges':'big','apples':'green','bananas':'fresh'}, 
{'oranges':'big','apples':'red'}, 
{'oranges':'big','apples':'green','bananas':'rotten'} 
] 

Jak zrobić to? Stukrotne dzięki!

+1

są to znaczy, jeśli krótszy słownik jest podzbiorem dłuższy słownika, następnie przesączyć go, prawda? –

+0

Pierwszym krokiem jest ustalenie, jak oznaczać coś jako częściowy duplikat. Czy to tylko kluczowa para pojawia się więcej niż raz? –

+0

@Shawn.tak jest. Dokładnie tak! –

Odpowiedz

3

Spróbuj po wprowadzeniu

Uwaga w moich realizacjach, jestem wstępnego sortowania i wybierając tylko te kombinacje par 2 do zmniejszenia liczby iteracji. To zapewni, że kluczem jest zawsze mniejsza lub równa wielkości do siana

>>> my_list =[ 
{'oranges':'big','apples':'green'}, 
{'oranges':'big','apples':'green','bananas':'fresh'}, 
{'oranges':'big','apples':'red'}, 
{'oranges':'big','apples':'green','bananas':'rotten'} 
] 

#Create a function remove_dup, name it anything you want 
def remove_dup(lst): 
    #import combinations for itertools, mainly to avoid multiple nested loops 
    from itertools import combinations 
    #Create a generator function dup_gen, name it anything you want 
    def dup_gen(lst): 
     #Now read the dict pairs, remember key is always shorter than hay in length 
     for key, hay in combinations(lst, 2): 
      #if key is in hay then set(key) - set(hay) = empty set 
      if not set(key) - set(hay): 
       #and if key is in hay, yield it 
       yield key 
    #sort the list of dict based on lengths after converting to a item tuple pairs 
    #Handle duplicate elements, thanks to DSM for pointing out this boundary case 
    #remove_dup([{1:2}, {1:2}]) == [] 
    lst = sorted(set(tuple(e.items()) for e in lst), key = len) 
    #Now recreate the dictionary from the set difference of 
    #the original list and the elements generated by dup_gen 
    #Elements generated by dup_gen are the duplicates that needs to be removed 
    return [dict(e) for e in set(lst) - set(dup_gen(lst))] 

remove_dup(my_list) 
[{'apples': 'green', 'oranges': 'big', 'bananas': 'fresh'}, {'apples': 'green', 'oranges': 'big', 'bananas': 'rotten'}, {'apples': 'red', 'oranges': 'big'}] 

remove_dup([{1:2}, {1:2}]) 
[{1: 2}] 

remove_dup([{1:2}]) 
[{1: 2}] 

remove_dup([]) 
[] 

remove_dup([{1:2}, {1:3}]) 
[{1: 2}, {1: 3}] 

Szybszy realizacji

def remove_dup(lst): 
    #sort the list of dict based on lengths after converting to a item tuple pairs 
    #Handle duplicate elements, thanks to DSM for pointing out this boundary case 
    #remove_dup([{1:2}, {1:2}]) == [] 
    lst = sorted(set(tuple(e.items()) for e in lst), key = len) 
     #Generate all the duplicates 
    dups = (key for key, hay in combinations(lst, 2) if not set(key).difference(hay)) 
    #Now recreate the dictionary from the set difference of 
    #the original list and the duplicate elements 
    return [dict(e) for e in set(lst).difference(dups)] 
+1

@MostafaR: {'a': 'b', 'a': 'b'} jest faktycznie {' a ':' b '} i według teorii mnogości zestaw jest podzbiorem samego siebie – Abhijit

+1

@MostafaR: '{' a ':' b ',' a ':' b '} == {' a ':' b ' } '. – Blender

+0

Wielkie dzięki, działa świetnie! –

2

Oto jeden realizacja można użyć: -

>>> my_list = [ 
{'oranges':'big','apples':'green'}, 
{'oranges':'big','apples':'green','bananas':'fresh'}, 
{'oranges':'big','apples':'red'}, 
{'oranges':'big','apples':'green','bananas':'rotten'} 
] 

>>> def is_subset(d1, d2): 
     return all(item in d2.items() for item in d1.items()) 
     # or 
     # return set(d1.items()).issubset(set(d2.items())) 

>>> [d for d in my_list if not any(is_subset(d, d1) for d1 in my_list if d1 != d)] 
[{'apples': 'green', 'oranges': 'big', 'bananas': 'fresh'}, 
{'apples': 'red', 'oranges': 'big'}, 
{'apples': 'green', 'oranges': 'big', 'bananas': 'rotten'}] 

Dla każdego dyktafonu d w my_list: -

any(is_subset(d, d1) for d1 in my_list if d1 != d) 

sprawdza, czy jest podzbiorem drugiego dict w my_list. Jeśli zwróci ona True, to istnieje co najmniej jeden dykt, którego podzbiór to d. Więc bierzemy not z niego, aby wykluczyć d z listy.

+0

Wielkie dzięki, działa świetnie! –

1

Krótka odpowiedź

def is_subset(d1, d2): 
    # Check if d1 is subset of d2 
    return all(item in d2.items() for item in d1.items()) 

filter(lambda x: len(filter(lambda y: is_subset(x, y), my_list)) == 1, my_list) 
+0

To naprawdę sprytne, jak na świecie wymyśliłeś to? – george

+0

Twoja odpowiedź nie różni się zbytnio od Rohita, z wyjątkiem tego, że zasłoniłeś ją wieloma filtrami. – Abhijit

5

Pierwszy [dobrze, po drugie, z pewnymi modyfikacjami ..] rzeczą, która przychodzi do głowy to:

def get_superdicts(dictlist): 
    superdicts = [] 
    for d in sorted(dictlist, key=len, reverse=True): 
     fd = set(d.items()) 
     if not any(fd <= k for k in superdicts): 
      superdicts.append(fd) 
    new_dlist = map(dict, superdicts) 
    return new_dlist 

co daje:

>>> a = [{'apples': 'green', 'oranges': 'big'}, {'apples': 'green', 'oranges': 'big', 'bananas': 'fresh'}, {'apples': 'red', 'oranges': 'big'}, {'apples': 'green', 'oranges': 'big', 'bananas': 'rotten'}] 
>>> 
>>> get_superdicts(a) 
[{'apples': 'red', 'oranges': 'big'}, 
{'apples': 'green', 'oranges': 'big', 'bananas': 'rotten'}, 
{'bananas': 'fresh', 'oranges': 'big', 'apples': 'green'}] 

[Oryginalnie używałem tutaj frozenset, myśląc, że mógłbym wykonać jakąś inteligentną operację, ale oczywiście nie zrobił " t wymyślić cokolwiek]

+0

Możesz zamienić 'fd.issubset (k)' na 'fd <= k'. – Blender

+0

@Blender: dobry punkt, edytowane. Nadal wydaje się, że powinien istnieć jakiś trik oparty na slickerach. – DSM

1

myślę, że ma lepszy porządek czasowy.

def is_subset(a, b): 
    return not set(a) - set(b) 

def remove_extra(my_list): 
    my_list = [d.items() for d in my_list] 
    my_list.sort() 

    result = [] 
    for i in range(len(my_list) - 1): 
     if not is_subset(my_list[i], my_list[i + 1]): 
      result.append(dict(my_list[i])) 
    result.append(dict(my_list[-1])) 

    return result 

print remove_extra([ 
     {'oranges':'big','apples':'green'}, 
     {'oranges':'big','apples':'green','bananas':'fresh'}, 
     {'oranges':'big','apples':'red'}, 
     {'oranges':'big','apples':'green','bananas':'rotten'} 
    ]) 
Powiązane problemy