Иногда я обнаруживаю, что делаю что-то, что изменяет только часть имеющихся у меня данных. Например:
c = {'substructure': merge(a['substructure'], b['substructure'])
Есть ли способ избежать этого повторения?
Добавим некоторые детали, это из ответ я написал на ТАК:
from itertools import groupby, chain
from collections import ChainMap
a = {
'folders': [
{ 'id': 124, 'name': 'Leads', 'totalBlacklisted': 0, 'uniqueSubscribers': 0, 'totalSubscribers': 0 },
{ 'id': 123, 'name': 'Alumni', 'totalBlacklisted': 0, 'uniqueSubscribers': 0, 'totalSubscribers': 0 },
]
}
b = {
'folders': [
{ 'id': 124, 'name': 'Leads' },
{ 'id': 121, 'name': 'Member' },
{ 'id': 123, 'name': 'Alumni' }
]
}
def key(x):
return (x['id'], x['name'])
def merge(a, b, key):
groups = groupby(sorted(chain(a, b), key=key), key=key)
merged = [dict(ChainMap(*g)) for _, g in groups]
return merged
Затем применить a
& b
для слияния вы должны сделать это:
print({'folders': merge(a['folders'], b['folders'], key=key)})
В центре моего вопроса тот факт, что merge
не нужно очищать данные.
1 ответ
Вопреки моему здравому смыслу, я собираюсь угадать, что вы на самом деле хотите сделать.
Сделайте нисходящий путь в вашей функции слияния, и избыточность исчезнет. В следующем примере два выхода равны:
from itertools import groupby, chain
from collections import ChainMap
from pprint import pprint
from typing import Iterable, Sequence
def merge(a, b, key):
groups = groupby(sorted(chain(a, b), key=key), key=key)
merged = [dict(ChainMap(*g)) for _, g in groups]
return merged
def merge2(a: dict, b: dict, path: Iterable, keys: Sequence) -> dict:
"""
:param a: Superior dictionary; this takes precedence
:param b: Inferior dictionary
:param path: The key path through both dictionaries to get to the merging dict
:param keys: A sequence of equivalence keys upon which merging is done
:return: The merged dictionary, contained by any wrapping dicts via path
"""
def key_fun(inner: dict) -> tuple:
return tuple(inner[k] for k in keys)
# Descend to the dicts to be merged
for p in path:
a = a[p]
b = b[p]
# Merge the inner dicts
groups = groupby(sorted(chain(a, b), key=key_fun), key=key_fun)
merged = [dict(ChainMap(*g)) for _, g in groups]
# Wrap the dict
for k in reversed(path):
merged = {k: merged}
return merged
def main():
def key(x):
return x['id'], x['name']
a = {
'folders': [
{'id': 124, 'name': 'Leads', 'totalBlacklisted': 0, 'uniqueSubscribers': 0, 'totalSubscribers': 0},
{'id': 123, 'name': 'Alumni', 'totalBlacklisted': 0, 'uniqueSubscribers': 0, 'totalSubscribers': 0},
]
}
b = {
'folders': [
{'id': 124, 'name': 'Leads', 'totalBlacklisted': 3, 'foob': 2},
{'id': 121, 'name': 'Member'},
{'id': 123, 'name': 'Alumni'}
]
}
pprint({'folders': merge(a['folders'], b['folders'], key=key)})
pprint(merge2(a, b, ('folders',), ('id', 'name')))
if __name__ == '__main__':
main()