【问题标题】:Comparing multiple dictionaries in Python在 Python 中比较多个字典
【发布时间】:2010-09-23 19:06:21
【问题描述】:

我是 Python 新手,遇到了一个无法通过 Google 搜索解决的问题。我已经使用 wxPython 和 ObjectiveListView 构建了一个 GUI。在其最中心,GUI 有一个列表控件,以 X 行(数据由用户加载)和五列显示数据。

当用户从列表控件中选择多个条目(按 CTRL 或单击时按住 shift)时,ObjectiveListView 模块会为我提供一个字典列表,这些字典包含列表控件行中的数据。这正是我想要的,很好!

返回的列表如下所示:

print MyList
[{'id':1023, 'type':'Purchase', 'date':'23.8.2008', 'sum':'-21,90', 'target':'Apple Store'}, {'id':1024, 'type':'Purchase', 'date':'24.8.2008', 'sum':'-21,90', 'target':'Apple Store'}, {'id':23, 'type':'Purchase', 'date':'2.8.2008', 'sum':'-21,90', 'target':'Apple Store'}]

所有字典都有相同的键,但值会发生变化。 'id' 值是唯一的。问题从这里开始。我想获取用户选择的所有项目的通用值。在上面的列表中,它们将是 'sum':'-21,90' 和 'target':'Apple Store'。

我不知道如何正确比较列表中的字典。一个大问题是我事先不知道列表包含多少个字典,因为它是由用户决定的。

我有一个模糊的想法,列表推导将是要走的路,但我只知道如何将两个列表与列表推导进行比较,而不是 n 个列表。任何帮助将不胜感激。

【问题讨论】:

  • 'type':'Purchase' 不也是常用值之一吗?
  • 您对“共同价值观”的定义是什么?你的意思是你想得到某些键值的结果,同时排除其他的?或者,检索多次出现的值的结果?

标签: python data-mining


【解决方案1】:

对不起,是的,'type':'Purchase'也是常用值之一。应该已经登录才能编辑问题。

【讨论】:

    【解决方案2】:
    >>> mysets = (set(x.items()) for x in MyList)
    >>> reduce(lambda a,b: a.intersection(b), mysets)
    set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])
    

    首先,我创建了一个生成器,它将字典列表转换为键值对集合的可迭代序列。您可以在此处使用列表推导,但这种方式不会将您的整个列表转换为另一个列表,如果您不知道它有多大,这很有用。

    然后我使用reduce 来应用一个函数来查找每个集合之间的共同值。它找到集合 1 和集合 2 的交集,它本身就是一个集合,然后是那个集合和集合 3 的交集,等等。mysets 生成器会很高兴地将每个集合按需提供给 reduce 函数,直到它完成。

    我相信 reduce 作为 Python 3.0 的内置功能已被弃用,但在 functools 中应该仍然可用。

    您当然可以通过将reduce 中的mysets 替换为生成器表达式来使其成为单行代码,但这会降低IMO 的可读性。在实践中,我什至可能更进一步,将 lambda 分解为自己的行:

    >>> mysets = (set(x.items()) for x in MyList)
    >>> find_common = lambda a,b: a.intersection(b)
    >>> reduce(find_common, mysets)
    set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])
    

    如果你需要最终结果是一个字典,只需像这样包装它:

    >>> dict(reduce(find_common, mysets))
    {'sum': '-21,90', 'type': 'Purchase', 'target': 'Apple Store'}
    

    dict 可以接受任何键值对的迭代器,例如最后返回的元组集合。

    【讨论】:

    • 这里可能有点儿戏,但值得知道 set.intersection 将采用任何序列类型作为参数,因此您可以将整个事情简化为:reduce(lambda a,b: a.intersection(b.items()), MyList[1:], set(MyList[0].items()))
    • 请添加一个警告,即键和值应该是可散列的。通常情况下会这样,但让我们尽量减少意外的可能性。
    【解决方案3】:

    首先,我们需要一个函数来计算两个字典的交集:

    def IntersectDicts( d1, d2 ) :
        return dict(filter(lambda (k,v) : k in d2 and d2[k] == v, d1.items()))
    

    然后我们可以用它来处理任意数量的字典:

    result = reduce(IntersectDicts, MyList)
    

    【讨论】:

      【解决方案4】:

      我的回答与 Matthew Trevor 的回答相同,只是有一点不同:

      >>> mysets = (set(x.items()) for x in MyList)
      >>> reduce(set.intersection, mysets)
      set([('sum', '-21,90'), ('type', 'Purchase'), ('target', 'Apple Store')])
      

      这里我使用set.intersection 而不是创建一个新的 lambda。在我看来,这更具可读性,因为这直观地读作“减少使用集合交集运算符减少此列表”。这也应该更快,因为set.intersection 是一个内置的 C 函数。

      要完全回答您的问题,您可以使用列表推导式提取值:

      >>> mysets = (set(x.items()) for x in MyList)
      >>> result = reduce(set.intersection, mysets)
      >>> values = [r[1] for r in result]
      >>> values
      ['-21,90', 'Purchase', 'Apple Store']
      

      这对我来说是一行。但这完全取决于您:

      >>> [r[1] for r in reduce(set.intersection, (set(x.items()) for x in myList))]
      ['-21,90', 'Purchase', 'Apple Store']
      

      【讨论】:

      • 对于 3000 个项目的列表,你的版本比我的快 5-600 微秒,所以几乎没有“快得多”:)
      【解决方案5】:

      由于您只是在寻找公共集合,因此您可以将第一个字典中的键与所有其他字典中的键进行比较:

      common = {}
      for k in MyList[0]:
          for i in xrange(1,len(MyList)):
              if MyList[0][k] != MyList[i][k]: continue
              common[k] = MyList[0][k]
      
      >>> common
      {'sum': '-21,90', 'type': 'Purchase', 'target': 'Apple Store'}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-30
        • 1970-01-01
        • 2012-04-07
        • 2019-06-15
        • 1970-01-01
        • 1970-01-01
        • 2017-09-04
        • 2013-02-13
        相关资源
        最近更新 更多