Объединить два списка словарей на основе условия - PullRequest
0 голосов
/ 08 марта 2019

У меня есть два списка словарей, и мне нужно объединить их, когда USA и GOOG совпадают.

list1 = 
[{'USA': 'Eastern', 
  'GOOG': '2019', 
  'Up': {'Upfront': 45}, 
  'Right': {'Upfront': 12}}, 

 {'USA': 'Western', 
  'GOOG': '2019', 
  'Up': {'Upfront': 10}, 
  'Right': {'Upfront': 15}}]

list2=
[{'USA': 'Western', 
  'GOOG': '2019', 
  'Down': {'Downback': 35}, 
  'Right': {'Downback': 25}}, 

 {'USA': 'Eastern', 
  'GOOG': '2018', 
  'Down': {'Downback': 15}, 
  'Right': {'Downback': 55}}]

Поскольку USA и GOOG имели одинаковые значения для2-й элемент в list1 и 1-й элемент в list2, поэтому они должны быть объединены.Ожидаемый результат следующий:

Result = 
[{'USA': 'Eastern', 
  'GOOG': '2019', 
  'Up': {'Upfront': 45}, 
  'Right': {'Upfront': 12}}, 

 {'USA': 'Western', 
  'GOOG': '2019', 
  'Up': {'Upfront': 10}, 
  'Down': {'Downback': 35}, 
  'Right': {'Upfront': 15, 'Downback': 25}},

 {'USA': 'Eastern', 
  'GOOG': '2018', 
  'Down': {'Downback': 15}, 
  'Right': {'Downback': 55}}]

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

Моя попытка:

from collections import defaultdict
dics = list1+list2

for dic in dics:
    for key, val in dic.items():
        dd[key].append(val)            

for dic in dics:
    for key, val in dic.items(): 
        dd[key].append(val)

Ответы [ 3 ]

2 голосов
/ 08 марта 2019

В том, что вам нужно, есть две алгоритмические задачи: найдите записи с одинаковыми значениями для США и GOOGL, а затем соедините их и сделайте так, чтобы, если в обеих записях был один и тот же ключ, их значение объединялось.

Наивный подход для первого состоит в том, чтобы иметь цикл for, который будет повторять значения списка list1, и для каждого значения повторять все значения для списка list2 - два разделенных цикла не обрежут его, вы 'нам нужно два вложенных for цикла:

for element in list1:
    for other_element in list2:
        if ...:
            ...

Хотя этот подход будет работать и подходит для небольших списков (<1000 записей, например), он занимает некоторое количествовремя и ресурсы, которые пропорциональны квадрату размеров вашего списка, то есть для списков, которые близки к ~ 1000 элементов, мы говорим 1 миллион итераций.Если сами списки составляют 1 000 000 элементов, вычисление потребует 1 * 10 ^ 12 сравнений, а на современных компьютерах это вообще невозможно. </p>

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

Вторая часть вашей задачи - сравнить, скопировать одну запись в список результатов и обновитьключи в результирующей копии, так что любые дублирующие ключи объединяются.Чтобы избежать проблем при копировании первых записей, мы безопаснее используем Python copy.deepcopy, который гарантирует, что под-словари являются объектами, отличными от тех, что находятся в исходной записи, и останутся изолированными.

from copy import deepcopy

def merge_lists(list1, list2):
    # create dictionary from list1:
    dict1 = {(record["GOOG"], record["USA"]): record  for record in list1}

    #compare elements in list2 to those on list1:

    result = {}
    for record in list2:
        ckey = record["GOOG"], record["USA"]
        new_record = deepcopy(record)
        if ckey in dict1:
            for key, value in dict1[ckey].items():
                if key in ("GOOG", "USA"):
                    # Do not merge these keys
                    continue
                # Dict's "setdefault" finds a key/value, and if it is missing
                # creates a new one with the second parameter as value
                new_record.setdefault(key, {}).update(value)

        result[ckey] = new_record

    # Add values from list1 that were not matched in list2:
    for key, value in dict1.items():
        if key not in result:
            result[key] = deepcopy(value)

    return list(result.values())
1 голос
/ 08 марта 2019

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

def joinListByDictionary(list1, list2):
    """Join lists on USA and GOOG having the same value"""
    list1.extend(list2)
    matchIndx = []
    matches = []    

    for dicts in range(len(list1)):
        for dicts2 in range(len(list1)):
            if dicts == dicts2:
                continue
            if list1[dicts]["GOOG"] == list1[dicts2]["GOOG"] and list1[dicts]["USA"] == list1[dicts2]["USA"]:

                matches.append(list1[dicts])
                matchIndx.append(dicts) 
    for dictz in matches:
        for dictzz in matches:
            for key in dictz.keys():
                if key in dictzz.keys() and isinstance(dictzz[key], dict):
                    dictzz[key].update(dictz[key])          
        matches.remove(dictz)

    newList = [list1[ele] for ele in range(len(list1)) if ele not in matchIndx]
    newList.extend(matches)
    print newList
    return newList       

joinListByDictionary(list1, list2)
1 голос
/ 08 марта 2019

Вот моя попытка.Не уверен, что это лучший способ, но это начало.

Шаги:

  • объединить списки словарей
  • создать отсортированную коллекцию соответствующих значений ииндексировать в объединенном списке
  • группировать по соответствующим значениям
  • перебирать ключи и группы, добавляя словарь, если он появляется только один раз на основе совпадений значений, или обновлять словарь, если он появляется более одного раза на основепо значению соответствует

Код:

import operator as op
import itertools as it
from functools import reduce
from pprint import pprint

dictionaries = reduce(op.add, (list1, list2,))
groups = it.groupby(sorted([(op.itemgetter('USA', 'GOOG')(d), i)
                            for i, d in enumerate(dictionaries)]),
                    key=op.itemgetter(0))
results = []
for key, group in groups:
    _, indices = zip(*group)
    if len(indices) == 1:
        i, = indices
        results.append(dictionaries[i])
    else:
        merge = dictionaries[indices[0]]
        for i in indices[1:]:
            merge.update(dictionaries[i])
        results.append(merge)
pprint(results, indent=4)

ВЫХОД:

[{'Down': {'Downback': 15}, 'GOOG': '2018 ',' Right ': {' Downback ': 55},' USA ':' Eastern '}, {' GOOG ':' 2019 ',' Right ': {' Upfront ': 12},' USA ':'Восточный ',' Вверх ': {' Upfront ': 45}}, {' Down ': {' Downback ': 35},' GOOG ':' 2019 ',' Right ': {' Downback ': 25},'США ':' Западный ',' Вверх ': {' Вверх ': 10}}]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...