Python: как объединить dict в список dict на основе значения - PullRequest
0 голосов
/ 04 мая 2018

У меня есть список диктов, где каждый дикт состоит из 3 клавиш: name, url и location.
Только значение 'name' может быть одинаковым во всех словах, а 'url' и 'location' всегда различаются по всему списку.

Пример:

[
{"name":"A1", "url":"B1", "location":"C1"}, 
{"name":"A1", "url":"B2", "location":"C2"}, 
{"name":"A2", "url":"B3", "location":"C3"},
{"name":"A2", "url":"B4", "location":"C4"}, ...
]  

Затем я хочу сделать их группировкой на основе значения в «name» следующим образом.

Ожидаемая:

[
{"name":"A1", "url":"B1, B2", "location":"C1, C2"},
{"name":"A2", "url":"B3, B4", "location":"C3, C4"},
]

(фактический список состоит из> 2000 диктов)

Я был бы очень рад, чтобы решить эту ситуацию.
Любые советы / ответы будут с благодарностью.

Заранее спасибо.

Ответы [ 6 ]

0 голосов
/ 04 мая 2018

Вот вариант ( трудно даже прочитать , мне кажется, что я царапаю правую сторону головы левой рукой, но на данный момент я не знаю, как сделать его короче) который использует:

>>> pprint.pprint(initial_list)
[{'location': 'C1', 'name': 'A1', 'url': 'B1'},
 {'location': 'C2', 'name': 'A1', 'url': 'B2'},
 {'location': 'C3', 'name': 'A2', 'url': 'B3'},
 {'location': 'C4', 'name': 'A2', 'url': 'B4'}]
>>>
>>> NAME_KEY = "name"
>>>
>>> final_list = [list(itertools.accumulate(group_list, func=lambda x, y: {key: x[key] if key == NAME_KEY else " ".join([x[key], y[key]]) for key in x}))[-1] \
...     for group_list in [list(group[1]) for group in itertools.groupby(sorted(initial_list, key=lambda x: x[NAME_KEY]), key=lambda x: x[NAME_KEY])]]
>>>
>>> pprint.pprint(final_list)
[{'location': 'C1 C2', 'name': 'A1', 'url': 'B1 B2'},
 {'location': 'C3 C4', 'name': 'A2', 'url': 'B3 B4'}]

Обоснование (от наружный до внутренний ):

  • Сгруппируйте словари в исходном списке на основе их значения, соответствующего ключу name (itertools.groupby)
    • Для правильной работы вспомогательной операции необходимо отсортировать список по тому же значению до группировки (sorted)
  • Для каждой такой группы словарей выполнить " sum " (itertools.accumulate)
    • func аргумент " сумма с" 2 словаря, основанные на ключах:
      • Если ключ name , просто возьмите значение из словаря 1 st (в любом случае оно одинаково для обоих словарей)
      • В противном случае просто добавьте 2 значения (строки) с пробелом между

Вопросы

  • Словари должны оставаться однородными (все должны иметь одинаковую структуру (ключи))
  • Жестко закодирована только клавиша name (но если вы решите добавить другие клавиши, не являющиеся строками, вам придется также настроить func)
  • Это может быть разделено для удобства чтения
  • Не уверен насчет lambda s (по производительности)
0 голосов
/ 04 мая 2018

Поскольку ваш набор данных относительно невелик, я полагаю, что сложность по времени здесь не так уж важна, поэтому вы можете рассмотреть следующий код.

from collections import defaultdict
given_data = [
    {"name":"A1", "url":"B1", "location":"C1"}, 
    {"name":"A1", "url":"B2", "location":"C2"}, 
    {"name":"A2", "url":"B3", "location":"C3"},
    {"name":"A2", "url":"B4", "location":"C4"},
] 
D = defaultdict(list)
for item in given_data:
    D[item['name']].append(item)
result = []
for x in D:
    urls = ""
    locations = ""
    for pp in D[x]:
        urls += pp['url']+" "
        locations += pp['location']+" "
    result.append({'name': x, 'url': urls.strip(), 'location': locations.strip()})
0 голосов
/ 04 мая 2018

, где res:

[{'location': 'C1', 'name': 'A1', 'url': 'B1'},
 {'location': 'C2', 'name': 'A1', 'url': 'B2'},
 {'location': 'C3', 'name': 'A2', 'url': 'B3'},
 {'location': 'C4', 'name': 'A2', 'url': 'B4'}]

Вы можете работать с данными, используя defaultdict и распаковывая результат в список для понимания:

from collections import defaultdict

result = defaultdict(lambda: defaultdict(list))

for items in res:
     result[items['name']]['location'].append(items['location'])
     result[items['name']]['url'].append(items['url'])

final = [
    {'name': name, **{inner_names: ' '.join(inner_values) for inner_names, inner_values in values.items()}}
    for name, values in result.items()
]

А final составляет:

In [57]: final
Out[57]:
[{'location': 'C1 C2', 'name': 'A1', 'url': 'B1 B2'},
 {'location': 'C3 C4', 'name': 'A2', 'url': 'B3 B4'}]
0 голосов
/ 04 мая 2018

Как то так? Небольшое отклонение: я предпочел хранить URL и местоположения в списке внутри resDict , а не в приложении 1012 * ул. * * * 1013 myDict = [ {"name":"A1", "url":"B1", "location":"C1"}, {"name":"A1", "url":"B2", "location":"C2"}, {"name":"A2", "url":"B3", "location":"C3"}, {"name":"A2", "url":"B4", "location":"C4"} ] resDict = [] def getKeys(d): arr = [] for row in d: arr.append(row["name"]) ret = list(set(arr)) return ret def filteredDict(d, k): arr = [] for row in d: if row["name"] == k: arr.append(row) return arr def compressedDictRow(rowArr): urls = [] locations = [] name = rowArr[0]['name'] for row in rowArr: urls.append(row['url']) locations.append(row['location']) return {"name":name,"urls":urls, "locations":locations} keys = getKeys(myDict) for key in keys: rowArr = filteredDict(myDict,key) row = compressedDictRow(rowArr) resDict.append(row) print(resDict) Выходы (в одну строку):

[
    {'name': 'A2', 'urls': ['B3', 'B4'], 'locations': ['C3', 'C4']}, 
    {'name': 'A1', 'urls': ['B1', 'B2'], 'locations': ['C1', 'C2']}
]
0 голосов
/ 04 мая 2018

Используя комментарий Ярослава Суржикова, вот решение с использованием itertools.groupby

from itertools import groupby

dicts = [
    {"name":"A1", "url":"B1", "location":"C1"},
    {"name":"A1", "url":"B2", "location":"C2"},
    {"name":"A2", "url":"B3", "location":"C3"},
    {"name":"A2", "url":"B4", "location":"C4"},
]

def merge(dicts):
    new_list = []
    for key, group in groupby(dicts, lambda x: x['name']):
        new_item = {}
        new_item['name'] = key
        new_item['url'] = []
        new_item['location'] = []
        for item in group:
            new_item['url'].extend([item.get('url', '')])
            new_item['location'].extend([item.get('location', '')])
        new_item['url'] = ', '.join(new_item.get('url', ''))
        new_item['location'] = ', '.join(new_item.get('location', ''))
        new_list.append(new_item)
    return new_list

print(merge(dicts))
0 голосов
/ 04 мая 2018

Со вспомогательной группой dict (для Python> 3.5):

data = [
    {"name":"A1", "url":"B1", "location":"C1"}, 
    {"name":"A1", "url":"B2", "location":"C2"}, 
    {"name":"A2", "url":"B3", "location":"C3"},
    {"name":"A2", "url":"B4", "location":"C4"}
]

groups = {}
for d in data:
    if d['name'] not in groups:
        groups[d['name']] = {'url': d['url'], 'location': d['location']}
    else:
        groups[d['name']]['url'] += ', ' + d['url']
        groups[d['name']]['location'] += ', ' + d['location']
result = [{**{'name': k}, **v} for k, v in groups.items()]

print(result)

Выход:

[{'name': 'A1', 'url': 'B1, B2', 'location': 'C1, C2'}, {'name': 'A2', 'url': 'B3, B4', 'location': 'C3, C4'}]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...