Python - 如何计算两个字典相等部分?

4

我在合并或计算这两个字典的共同/相等部分方面遇到了问题。在我的字典中,值是列表:

d1 = {0:['11','18','25','38'], 
      1:['11','18','25','38'], 
      2:['11','18','25','38'], 
      3:['11','18','25','38']}

d2 = {0:['05','08','11','13','16','25','34','38','40', '43'], 
      1:['05', '08', '09','13','15','20','32','36','38', '40','41'], 
      2:['02', '08', '11', '13', '18', '20', '22','33','36','39'], 
      3:['06', '11', '12', '25', '26', '27', '28', '30', '31', '37']}

我想检查"d2",并了解是否有来自"d1"的数字。如果有,我想用新数据更新其中一个数字,或者接收第三个字典"d3",其中只包含在"d1"和"d2"中相同/相等的值,例如:

d3 = {0:['11','25','38'], 1:['38'], 2:['11','18'], 3:['11','25']} 

有人能帮我解决这个问题吗?

我的错,我忘记更具体一些了。我正在寻找Python的解决方案。


请指定您的编程语言,或者如果您不在意的话。您的示例可以采用伪代码或某种现有语言中的字面量;最好知道您期望什么。 - unwind
您基本上是要求对集合交叉进行一些迭代。这非常依赖于编程语言,请指定相关的编程语言。 - Yuval F
d3的答案对我来说看起来是错误的。 - S.Lott
我添加了Python标签,因为从我的评论中可以明显看出他在谈论Python。 - John Feminella
@Adrian: d3应该是{0:['11','25','38'],1:['38'],2:['11','18'],3:['11','25']}。 - jfs
显示剩余2条评论
5个回答

7
假设这是Python代码,你需要的是:
dict((x, set(y) & set(d1.get(x, ()))) for (x, y) in d2.iteritems())

生成结果字典"d3"。

Python 3.0+版本

>>> d3 = {k: list(set(d1.get(k,[])).intersection(v)) for k, v in d2.items()}
{0: ['11', '25', '38'], 1: ['38'], 2: ['11', '18'], 3: ['11', '25']}

上述版本(以及Python 2.x版本)允许空交集,因此在一般情况下需要进行额外的过滤:
>>> d3 = {k: v for k, v in d3.items() if v}

将上述内容一次性结合:

d3 = {}
for k, v in d2.items():
    # find common elements for d1 & d2
    v3 = set(d1.get(k,[])).intersection(v)
    if v3: # whether there are common elements
       d3[k] = list(v3) 

[编辑:我将此帖子设为社区维基,以便人们可以在需要时进行改进。如果您不习惯使用Python阅读此类内容,则可能会有些难以理解。]



抱歉,有个打字错误。你想使用 "d2.iteritems()" 来获取那里的项目列表。 - John Feminella
这个返回值是:{0: set(['11', '25', '38']), 1: set(['38']), 2: set(['11', '18']), 3: set(['11', '25'])},所以它非常接近。 - Adrian Archer
dict((x, list(set(y) & set(d1.get(x, ())))) for (x, y) in d2.iteritems()) 如果你更喜欢的话,这将返回一个列表的字典,而不是集合。 - Adrian Archer
令人印象深刻,但我认为聪明的一行代码并不特别符合Python的风格。 - dan-gph
我已经添加了Python 3.0+版本。 - jfs
显示剩余5条评论

4

提供一个更易读的解决方案:

d3= {}
for common_key in set(d1) & set(d2):
    common_values= set(d1[common_key]) & set(d2[common_key])
    d3[common_key]= list(common_values)

建议后编辑:

如果您只想要具有至少一个共同值项的键:

d3= {}
for common_key in set(d1) & set(d2):
    common_values= set(d1[common_key]) & set(d2[common_key])
    if common_values:
        d3[common_key]= list(common_values)

如果顺序和重复不重要,您可以将d1和d2的值保留为集合而不是列表。


+1:为了可读性,我会删除“common_”前缀并用显式的“intersection()”调用替换“&”(但这是品味问题)。 - jfs
顺便提一下,在一般情况下,common_values 可能是一个空集合。OP 问道:“是否存在某些值”,即 all(d3.values()) 必须为 True。 - jfs

1
如果我们可以假设d1和d2具有相同的键:
d3 = {}
for k in d1.keys():
    intersection = set(d1[k]) & set(d2[k])
    d3[k] = [x for x in intersection]

否则,如果我们不能假设这一点,那么情况会变得有些混乱:
d3 = {}
for k in set(d1.keys() + d2.keys()):
    intersection = set(d1.get(k, [])) & set(d2.get(k, []))
    d3[k] = [x for x in intersection]

编辑: 考虑到评论,新版本只检查d1和d2共有的键,这似乎是发布者所要求的。

d3 = {}
for k in set(d1.keys()) & set(d2.keys()):
    intersection = set(d1[k]) & set(d2[k])
    d3[k] = list(intersection)

@SilentGhost,为什么?能否详细说明一下?或者您可以发一个更Pythonic的代码。 - dan-gph
我已经点赞了约翰的回答,尽管我自己写的是:{i: list(set(j) & set(d1.get(i, []))) for i, j in d2.items()}。 - SilentGhost
@SilentGhost,谢谢,我不知道。这很容易解决。尽管修复使我的代码有点啰嗦。 - dan-gph
你的版本允许空交集,例如all(d3.values())在某些情况下可能为False。但是OP要求仅在存在一些公共数字时才包括这些键。 - jfs
bwt,list(some_set)运行得很好。我在Python 2.5+中进行了测试。 - jfs
显示剩余6条评论

1
问题归结为确定两个条目之间的公共元素。(要获取所有条目的结果,只需将代码放入循环中。)此外,每个条目都似乎是一个集合(即它没有重复元素)。因此,你需要做的就是找到这些元素之间的集合交集。许多语言都提供了一种方法或函数来实现这一点;例如,在C++中使用set容器和set_intersection函数。这比将一个集合中的每个元素与另一个进行比较要高效得多,正如其他人所建议的那样。

-1

伪代码中:

Dictionary d3 = new Dictionary()
for (i = 0 to min(d1.size(), d2.size()))
{
  element shared = getSharedElements(d1[i], d2[i]);
  d3.store(i, shared);
}

function getsharedElements(array e1, array e2)
{
  element e3 = new element();
  for (int i = 0 to e1.length)
  {
    if (e2.contains(e1[i]))
    {
      e3.add[e1[i]];
    }
  }
  return e3;
}

哈哈,Python实际上比伪代码更简洁!“Python是可执行的伪代码”,确实如此! - bobince
1
这篇文章是在原始帖子提到Python之前发布的。 - tehvan

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