Python zip по ключу



Я хотел бы объединить (zip?) два списка кортежей python, но совпадающих по ключу.



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



lst1 = [(0, 1.1), (1, 1.2), (2, 1.3),           (5, 2.5)]
lst2 = [ (1, 4.5), (2, 3.4), (4, 2.3), (5, 3.2)]

desiredOutput = [(1, 1.2, 4.5), (2, 1.3, 3.4), (5, 2.5, 3.2)]


Я мог бы сделать это очень беспорядочно и вручную с петлями, но я полагаю, что должны быть некоторые функции itertools / zipping, которые значительно упростят это.



Я уверен, что ответ существует и очевиден, просто у меня нет подходящей терминологии для его поиска.

==



(( Как бы то ни было, вот мое наивное решение. Я надеялся найти что-то аккуратнее / более подходящие для Python:



def key_zipper(lst1, lst2):    
dict1 = dict(lst1)
dict2 = dict(lst2)

intersectKeys = [k for k in dict1.keys() if k in dict2.keys()]

output = []

for key in intersectKeys:
output.append((key, dict1[key], dict2[key]))

return output


Спасибо ))

518   4  

4 ответов:

>>> [(i, a, b) for i, a in lst1 for j, b in lst2 if i==j]
[(1, 1.2, 4.5), (2, 1.3, 3.4), (5, 2.5, 3.2)]

Все еще немного грязно, но работает:

def combine(lst1, lst2):
  d2 = dict(lst2)
  return [(k, v, d2[k]) for (k, v) in lst1 if k in d2]

Обновление :

Если бы я действительно использовал это в производственном коде, я бы немного рефакторировал:

def dict_intersection(d1, d2):
    return [(k,v,d2[k]) for (k,v) in d1.items() if k in d2]

И тогда в вашем случае я бы позвонил

lst1 = [(0, 1.1), (1, 1.2), (2, 1.3), (5, 2.5)]
lst2 = [(1, 4.5), (2, 3.4), (4, 2.3), (5, 3.2)]
common = dict_intersection(dict(lst1), dict(lst2))

Ответ@Vincent-тоже хороший вариант.

Преобразуйте второй список в словарь, после чего вы можете проверить, присутствует ли ключ, не повторяя весь второй список:

def func(lst1,lst2):
    d2 = dict(lst2)
    return [(k,a,d2[k]) for (k,a) in lst1 if d2.has_key(k) ]

Решение с использованием itertools.groupby и еще heapq.merge:

from itertools import groupby
from heapq import merge
from operator import itemgetter

def key_zipper(*lst):
    for k, v in groupby(merge(*lst), itemgetter(0)):
        yield (k,) + tuple(map(itemgetter(1), v))

lst1 = [(0, 1.1), (1, 1.2), (2, 1.3),           (5, 2.5)]
lst2 = [          (1, 4.5), (2, 3.4), (4, 2.3), (5, 3.2)]
print(list(key_zipper(lst1, lst2)))
# [(0, 1.1), (1, 1.2, 4.5), (2, 1.3, 3.4), (4, 2.3), (5, 2.5, 3.2)]

merge и groupby оба нуждаются в сортировке входных данных. Если вы не всегда сортируете список, то вам нужно обязательно сделать это:

def key_zipper(*lst):
    for k, v in groupby(merge(*map(sorted, lst)), itemgetter(0)):
        yield (k,) + tuple(map(itemgetter(1), v))

Это имеет то преимущество, что он работает с произвольным количеством входных списков, и имеет лучшее время выполнения для больших списков.

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

Comments

    Ничего не найдено.