Как объединить вложенный словарь с другим вложенным словарем, но только тогда, когда у каждого есть совпадающее значение?

1

Лучший способ, которым я могу описать то, что я хотел бы достичь, - это обратиться к тому, как работает функция SQL INNER JOIN для отображения данных из двух таблиц, определяемых соответствующим именем столбца.

Я хотел бы получить аналогичную функцию, хотя, используя Python (предпочтительно 3.x), и вместо таблиц с соответствующим именем столбца я хотел бы объединить все два словаря вместе на основе сопоставления {k: v}.

Например...

lst_1 = [
    {
        'City'      :   'Boston',
        'State'     :   'Massechusets',
        'Name'      :   'Kim Tuttles',
        'Country'   :   'United State'
    },
    {
        'City'      :   'Portland',
        'Name'      :   'Larry Bird',
        'State'     :   'Oregon'
    },
    {
        'City'      :   'Chicago',
        'Name'      :   'John Jacobs',
        'State'     :   'Illinois'
    }
]

lst_2 = [
    {
        'Hobby'     :   'Tennis',
        'Build'     :   'Athletic',
        'Height'    :   'Six Feet, One Inch',
        'Name'      :   'Kim Tuttles',
        'Birthplace':   'Italy'
    },
    {
        'Name'      :   'John Jacobs',
        'Hobby'     :   'Baseball',
        'Build'     :   'Muscular',
        'Height'    :   'Five Feet, Eight Inches'
    }
]

Я хотел бы найти способ объединить словари из каждого списка, но только там, где найдена соответствующая пара {Key: Value}. Результат будет выглядеть так...

merged_lst = [
    {
        'Hobby'     :   'Tennis',
        'Build'     :   'Athletic',
        'Height'    :   'Six Feet, One Inch',
        'Birthplace':   'Italy'
        'City'      :   'Boston',
        'State'     :   'Massechusets',
        'Name'      :   'Kim Tuttles', # Merge on matching name
        'Country'   :   'United State'
    },
    {
        'Name'      :   'John Jacobs', # Merge on matching name
        'Hobby'     :   'Baseball',
        'Build'     :   'Muscular',
        'Height'    :   'Five Feet, Eight Inches'
        'City'      :   'Chicago',
        'State'     :   'Illinois'
    }
]

Мне удалось найти способ слияния словарей с использованием dict.update и zip(), хотя это было только при работе с двумя независимыми словарями, и это все еще было не совсем правильно. Я ценю любые советы и благодарю вас заранее.

  • 0
    Вы знаете соответствующий ключ ( 'Name' здесь) заранее?
  • 0
    К счастью, я согласен, и он одинаков для каждого словаря в любом списке. Так что в случае выше это будет 'Name' как вы упомянули.
Показать ещё 3 комментария
Теги:
python-3.x
dictionary
nested

5 ответов

5
Лучший ответ

В Python 3. 5+ мы можем уйти со следующим, игнорируя проблемы из дополнительных столкновений ключей.

k = 'Name'
merged_lst = [{**a, **b} for a in lst_1 for b in lst_2 if a[k]==b[k]]
  • {**a, **b} - это аккуратный способ распаковать два словаря, которые рассматриваются в комбинированном словаре (я считаю, что при столкновении он использует значение из b вместо a). Это единственный шаг, который требует 3. 5+. В Python 2.x со строковыми клавишами аналогичная конструкция - dict(a, **b), хотя Гвидо нахмурился. Другие варианты более подробные.
  • Сопоставление списков Python позволяет вам легко перебирать то, что эффективно является декартовым произведением lst_1 и lst_2, используя for два раза.
  • Мы заботимся только о словарях с тем же 'Name', поэтому бит a[k]==b[k].
  • Если вам разрешено клонировать любой из словарей в lst_1 или lst_2, подходы, связанные с dict.update(), скорее всего, быстрее. Они могут быть в любом случае, хотя синтаксис не так хорош, я не думаю.
  • 0
    Это очень аккуратно и лаконично. Однако следует отметить, что алгоритмическая сложность составляет O(m*n) во вложенном понимании, когда для рассматриваемой задачи O(m+n) возможна.
  • 0
    @schwobaseggl Это определенно может быть проблемой, но мне любопытно, как вы пришли к O(m+n) ? Это средний случай? В моей работе с нулями лучшее решение, которое я нашел, - O(m*log(m)+n*log(n)) худшем случае. Какой алгоритм лучше использовать для сложности времени здесь?
Показать ещё 4 комментария
1

вы можете сделать что-то вроде этого:

for l2 in lst_2:
   l2.update(next(l1 for l1 in lst_1 if l1["Name"] == l2["Name"]))
0

Это похоже на левое объединение в RDMS, например MySQL, и функцию $ lookup (aggregation) MongoDB. Вы можете изучить их для дальнейшего уточнения.

0

Одним из способов я сделал это:

Сначала создаются два словаря из списков с использованием имени в качестве ключа

lst1_dict={lst_1[i]["Name"]: lst_1[i] for i in range(len(lst_1))}
lst2_dict={lst_2[i]["Name"]: lst_2[i] for i in range(len(lst_2))}

{   'John Jacobs': {   'City': 'Chicago',
                   'Name': 'John Jacobs',
                   'State': 'Illinois'},
'Kim Tuttles': {   'City': 'Boston',
                   'Country': 'United State',
                   'Name': 'Kim Tuttles',
                   'State': 'Massechusets'},
'Larry Bird': {    'City': 'Portland', 
                   'Name': 'Larry Bird', 
                    'State': 'Oregon'}
}
{   
'John Jacobs': {   'Build': 'Muscular',
                   'Height': 'Five Feet, Eight Inches',
                   'Hobby': 'Baseball',
                   'Name': 'John Jacobs'},
'Kim Tuttles': {   'Birthplace': 'Italy',
                   'Build': 'Athletic',
                   'Height': 'Six Feett, One Inch',
                   'Hobby': 'Tennis',
                   'Name': 'Kim Tuttles'}
}

Получили общие имена в обоих

common_names=list(set(lst1_dict.keys())&set(lst2_dict.keys()))

Итерации по общим именам и их объединение

merged_list=[]
for name in common_names:
    lst1_val=lst1_dict[name]  #value from lst1 dictionary
    lst2_val=lst2_dict[name]  #value from lst2 dictionary
    #MERGE
    new_dict={}
    new_dict[name] = lst1_val.copy()
    new_dict[name].update(lst2_val)
    merged_list.append(new_dict[name])


[   {   'Birthplace': 'Italy',
    'Build': 'Athletic',
    'City': 'Boston',
    'Country': 'United State',
    'Height': 'Six Feett, One Inch',
    'Hobby': 'Tennis',
    'Name': 'Kim Tuttles',
    'State': 'Massechusets'},
{   'Build': 'Muscular',
    'City': 'Chicago',
    'Height': 'Five Feet, Eight Inches',
    'Hobby': 'Baseball',
    'Name': 'John Jacobs',
    'State': 'Illinois'}]
0

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

Функция:

def mergeSameNameDicts(l1,l2):

    duplicateNames = set ( p["Name"] for p in l1) & set( p["Name"] for p in l2) 

    import copy 
    rv = []        # collects enriched dicts
    for d in l1:
        if d["Name"] in duplicateNames:
            rv.append(copy.copy(d))           # copy dict over from l1

    for d in l2:                              # enhance with data from l2
        if (d["Name"] in duplicateNames):     # if name is a dupe. enhence all
            for d1 in rv:                     # dicts with that name inside rv
                if (d["Name"] == d1["Name"]): # the values of v2 will overwrite l1 if keys
                    d1.update(d)              # present in dicts of l1 and l2
    return rv

print(mergeSameNameDicts(lst_1,lst_2))

Выход:

[{'City': 'Boston',
  'State': 'Massechusets',
  'Name': 'Kim Tuttles',
  'Country': 'United State',
  'Hobby': 'Tennis',
  'Build': 'Athletic',
  'Height': 'Six Feett, One Inch',
  'Birthplace': 'Italy'},

 {'City': 'Chicago',
  'Name': 'John Jacobs',
  'State': 'Illinois',
  'Hobby': 'Baseball',
  'Build': 'Muscular',
  'Height': 'Five Feet, Eight Inches'}]
  • 0
    Интересно! Можете ли вы объяснить, что делает set в строке № 3? Я еще не знаком с этой функцией.
  • 0
    @trjv set () - неупорядоченная структура данных, в которой нет дубликатов. Он позволяет выполнять быстрые операции с теорией множеств, такие как объединение / различия / подмножество / суперсет / ... Если у вас было 2 списка по 200 диктов в каждом и только в 20 именах присутствуют оба, это вернет эти точные 20 имен, поэтому следующие циклы может зациклить только 20 чел и не все 200, так как они нам не нужны. Подробнее читайте здесь: ТАК Пост о set () и python set () doku

Ещё вопросы

Сообщество Overcoder
Наверх
Меню