Python按键进行zip操作

3
我希望能够在匹配键的情况下合并(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

感谢))


输出中的子元素需要是元组吗?使用列表会更容易。 - Daniel Roseman
@DanielRoseman 如果你能用列表做到这一点,我肯定可以处理它。 - jlb83
4个回答

12
>>> [(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)]

我之前不知道可以在列表推导式中使用多个 for,但我觉得这样很好。 - jlb83
1
@jlb83 为了效率起见,最好在实际情况下避免嵌套的 for 循环;正如其他答案所示,在这种情况下避免它是 _可能的。一个 n 长度的循环在一个 m 长度的循环中是 O(n*m);非嵌套算法是 O(n+m)。显然,即使对于较小的 n 和 m 值,也可以节省大量开销。 - PM 2Ring

4

还有点杂乱,但是能用:

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的回答也是一个很好的变化。

谢谢 - 这是我努力的更简洁版本(我刚刚在问题中发布了)。 - jlb83
4
从复杂性的角度来看,这比我的答案更好,因为使用了字典查找if k in d2。另外,您可以使用[(k, v, d2[k]) for k, v in d1.items() if k in d2]来保存查找。 - Vincent
2
你根本不需要 d1。 - Douglas Leeder

4

使用itertools.groupbyheapq.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)]

mergegroupby 都需要输入数据已排序。如果您的列表未始终排序,则需要确保进行排序:

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

这种方法的优点是可以处理任意数量的输入列表,并且在处理大型列表时具有更好的运行时间。

请注意,这里我将其编写为生成器,产生元组,而不是返回元组列表的函数,但将其转换为列表非常容易。


2
将第二个列表转换为字典,然后您可以在不遍历整个第二个列表的情况下检查键是否存在:
def func(lst1,lst2):
    d2 = dict(lst2)
    return [(k,a,d2[k]) for (k,a) in lst1 if d2.has_key(k) ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接