Python - сгладить список словарей
Список словарей:
data = [{
'a':{'l':'Apple',
'b':'Milk',
'd':'Meatball'},
'b':{'favourite':'coke',
'dislike':'juice'}
},
{
'a':{'l':'Apple1',
'b':'Milk1',
'd':'Meatball2'},
'b':{'favourite':'coke2',
'dislike':'juice3'}
}, ...
]
Мне нужно соединить все вложенные словари, чтобы достичь ожидаемого результата:
[{'d': 'Meatball', 'b': 'Milk', 'l': 'Apple', 'dislike': 'juice', 'favourite': 'coke'},
{'d': 'Meatball2', 'b': 'Milk1', 'l': 'Apple1', 'dislike': 'juice3', 'favourite': 'coke2'}]
Я пытаюсь вставить список вложенных списков, но не могу присоединиться к dict вместе:
L = [y for x in data for y in x.values()]
print (L)
[{'d': 'Meatball', 'b': 'Milk', 'l': 'Apple'},
{'dislike': 'juice', 'favourite': 'coke'},
{'d': 'Meatball2', 'b': 'Milk1', 'l': 'Apple1'},
{'dislike': 'juice3', 'favourite': 'coke2'}]
Я ищу быстрое решение.
Ответы
Ответ 1
Вы можете сделать следующее, используя itertools.chain
:
>>> from itertools import chain
# timeit: ~3.40
>>> [dict(chain(*map(dict.items, d.values()))) for d in data]
[{'l': 'Apple',
'b': 'Milk',
'd': 'Meatball',
'favourite': 'coke',
'dislike': 'juice'},
{'l': 'Apple1',
'b': 'Milk1',
'dislike': 'juice3',
'favourite': 'coke2',
'd': 'Meatball2'}]
Использование chain
, map
, *
делает это выражение сокращенным для следующего дважды вложенного понимания, которое на самом деле лучше работает в моей системе (Python 3.5.2) и не намного длиннее:
# timeit: ~2.04
[{k: v for x in d.values() for k, v in x.items()} for d in data]
# Or, not using items, but lookup by key
# timeit: ~1.67
[{k: x[k] for x in d.values() for k in x} for d in data]
Замечания:
Подход timeit: ~1.37
к петле и обновлению превосходит оба эти однострочных устройства в timeit: ~1.37
Ответ 2
Вы можете сделать это с помощью 2 вложенных циклов и dict.update()
чтобы добавить внутренние словари во временный словарь и добавить его в конец:
L = []
for d in data:
temp = {}
for key in d:
temp.update(d[key])
L.append(temp)
# timeit ~1.4
print(L)
Какие результаты:
[{'l': 'Apple', 'b': 'Milk', 'd': 'Meatball', 'favourite': 'coke', 'dislike': 'juice'}, {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2', 'favourite': 'coke2', 'dislike': 'juice3'}]
Ответ 3
Вы можете использовать functools.reduce
вместе с простым пониманием списка, чтобы сгладить список dicts
>>> from functools import reduce
>>> data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
>>> [reduce(lambda x,y: {**x,**y},d.values()) for d in data]
>>> [{'dislike': 'juice', 'l': 'Apple', 'd': 'Meatball', 'b': 'Milk', 'favourite': 'coke'}, {'dislike': 'juice3', 'l': 'Apple1', 'd': 'Meatball2', 'b': 'Milk1', 'favourite': 'coke2'}]
Тест времени следующий:
>>> import timeit
>>> setup = """
from functools import reduce
data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
"""
>>> min(timeit.Timer("[reduce(lambda x,y: {**x,**y},d.values()) for d in data]",setup=setup).repeat(3,1000000))
>>> 1.525032774952706
Тест времени других ответов на моей машине
>>> setup = """
data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
"""
>>> min(timeit.Timer("[{k: v for x in d.values() for k, v in x.items()} for d in data]",setup=setup).repeat(3,1000000))
>>> 2.2488374650129117
>>> min(timeit.Timer("[{k: x[k] for x in d.values() for k in x} for d in data]",setup=setup).repeat(3,1000000))
>>> 1.8990078769857064
>>> code = """
L = []
for d in data:
temp = {}
for key in d:
temp.update(d[key])
L.append(temp)
"""
>>> min(timeit.Timer(code,setup=setup).repeat(3,1000000))
>>> 1.4258553800173104
>>> setup = """
from itertools import chain
data = [{'b': {'dislike': 'juice', 'favourite': 'coke'}, 'a': {'l': 'Apple', 'b': 'Milk', 'd': 'Meatball'}}, {'b': {'dislike': 'juice3', 'favourite': 'coke2'}, 'a': {'l': 'Apple1', 'b': 'Milk1', 'd': 'Meatball2'}}]
"""
>>> min(timeit.Timer("[dict(chain(*map(dict.items, d.values()))) for d in data]",setup=setup).repeat(3,1000000))
>>> 3.774383604992181
Ответ 4
Если у вас есть вложенные словари с только клавишами "a" и "b", я предлагаю следующее решение, которое я нахожу быстро и очень легко понять (для удобства чтения):
L = [x['a'] for x in data]
b = [x['b'] for x in data]
for i in range(len(L)):
L[i].update(b[i])
# timeit ~1.4
print(L)