Python. Манипуляция со списком словарей
Друзья, у меня есть список словарей:
my_list =
[
{'oranges':'big','apples':'green'},
{'oranges':'big','apples':'green','bananas':'fresh'},
{'oranges':'big','apples':'red'},
{'oranges':'big','apples':'green','bananas':'rotten'}
]
Я хочу создать новый список, в котором будут устранены частичные дубликаты.
В моем случае этот словарь должен быть удален:
{'oranges':'big','apples':'green'}
поскольку он дублирует более длинные словари:
{'oranges':'big','apples':'green','bananas':'fresh'}
{'oranges':'big','apples':'green','bananas':'rotten'}
Следовательно, желаемый результат:
[
{'oranges':'big','apples':'green','bananas':'fresh'},
{'oranges':'big','apples':'red'},
{'oranges':'big','apples':'green','bananas':'rotten'}
]
Как это сделать? Спасибо миллион!
Ответы
Ответ 1
Первая [ну, вторая, с некоторыми изменениями..] вещь, которая приходит на ум, такова:
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
который дает:
>>> 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'}]
[Первоначально я использовал frozenset
здесь, думая, что могу сделать какую-то умную операцию набора, но, очевидно, ничего не придумал.]
Ответ 2
Попробуйте выполнить следующую реализацию
Обратите внимание, что в моих реализациях я предсказываю и выбираю только 2 пары комбинаций, чтобы уменьшить количество итераций.
Это гарантирует, что ключ всегда меньше или равен размеру сена
>>> 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}]
Более быстрая реализация
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)]
Ответ 3
Здесь вы можете использовать одну реализацию: -
>>> 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'}]
Для каждого dict d
в my_list
: -
any(is_subset(d, d1) for d1 in my_list if d1 != d)
проверяет, является ли это подмножеством любого другого dict
в my_list
. Если он возвращает True
, то существует, по крайней мере, один dict, подмножество которого d
. Итак, мы берем not
из этого, чтобы исключить d
из списка.
Ответ 4
Короткий ответ
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)
Ответ 5
Я думаю, что у него лучший порядок времени:
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'}
])