【问题标题】:Given a list of dictionaries, how can I eliminate duplicates of one key, and sort by another给定一个字典列表,我如何消除一个键的重复项,并按另一个键排序
【发布时间】:2012-02-03 04:50:34
【问题描述】:

我正在使用看起来像这样的 list 的 dict 对象(对象的顺序不同):

[
    {'name': 'Foo', 'score': 1},
    {'name': 'Bar', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3},
    {'name': 'Foo', 'score': 2},
    {'name': 'Baz', 'score': 2},
    {'name': 'Baz', 'score': 1},
    {'name': 'Bar', 'score': 1}
]

我想要做的是删除重复的名称,只保留每个名称中具有最高 'score' 的名称。上述列表的结果将是:

[
    {'name': 'Baz', 'score': 2},
    {'name': 'Foo', 'score': 3},
    {'name': 'Bar', 'score': 3}
]

我不确定在这里使用哪种模式(除了一个看似愚蠢的循环,它不断检查当前dict 的'name' 是否已经在列表中,然后检查它的'score' 是否高于现有的'score'。

【问题讨论】:

  • 跟着循环走,简单明了。
  • 当您需要“稍微”更改时,它简单明了,六个月后易于阅读
  • +1 这个问题有点神奇,因为它引出了一组多样且有趣的答案。令人着迷的是,这个问题有多少完全不同的解决方案。由于答案集丰富,我将其列为最爱(我也赞成每个具有创造性或有趣解决方案的答案)。
  • @Raymond - 谢谢。我也很高兴看到品种繁多。我有几个版本,但从列表中收集了一个更干净的版本。顺便说一句,我拥有并读过你的书(高级 Python,以防你有其他书),它很棒。我个人认为你应该再写一本书,也许是关于更高级的 Python 或 Python 中的设计模式。

标签: python algorithm list sorting


【解决方案1】:

一种方法是:

data = collections.defaultdict(list)
for i in my_list:
    data[i['name']].append(i['score'])
output = [{'name': i, 'score': max(j)} for i,j in data.items()]

所以输出将是:

[{'score': 2, 'name': 'Baz'},
 {'score': 3, 'name': 'Foo'},
 {'score': 3, 'name': 'Bar'}]

【讨论】:

    【解决方案2】:

    这里不需要默认字典或集合。您可以只使用简单的字典和列表。

    在字典中总结最好的跑分并将结果转换回列表:

    >>> s = [
        {'name': 'Foo', 'score': 1},
        {'name': 'Bar', 'score': 2},
        {'name': 'Foo', 'score': 3},
        {'name': 'Bar', 'score': 3},
        {'name': 'Foo', 'score': 2},
        {'name': 'Baz', 'score': 2},
        {'name': 'Baz', 'score': 1},
        {'name': 'Bar', 'score': 1}
    ]
    >>> d = {}
    >>> for entry in s:
            name, score = entry['name'], entry['score']
            d[name] = max(d.get(name, 0), score)
    
    >>> [{'name': name, 'score': score} for name, score in d.items()]
    [{'score': 2, 'name': 'Baz'}, {'score': 3, 'name': 'Foo'}, {'score': 3, 'name': 'Bar'}]
    

    【讨论】:

    • 如果我们使用像{'Foo': 3} 这样的数据结构而不是[{'name': 'Foo', 'score': 3}],这个解决方案将是最优雅的。我认为原始问题的发布者应该这样做。
    • 这是我最喜欢的解决方案。我唯一要改变的是 d.get(name,0) 到 d.get(name,score)。这也将允许负分数。
    【解决方案3】:

    只是为了好玩,这里是一个纯粹的函数式方法:

    >>> map(dict, dict(sorted(map(sorted, map(dict.items, s)))).items())
    [{'score': 3, 'name': 'Bar'}, {'score': 2, 'name': 'Baz'}, {'score': 3, 'name': 'Foo'}]
    

    【讨论】:

      【解决方案4】:

      排序是成功的一半。

      import itertools
      import operator
      
      scores = [
          {'name': 'Foo', 'score': 1},
          {'name': 'Bar', 'score': 2},
          {'name': 'Foo', 'score': 3},
          {'name': 'Bar', 'score': 3},
          {'name': 'Foo', 'score': 2},
          {'name': 'Baz', 'score': 2},
          {'name': 'Baz', 'score': 1},
          {'name': 'Bar', 'score': 1}
      ]
      
      result = []
      sl = sorted(scores, key=operator.itemgetter('name', 'score'),
        reverse=True)
      name = object()
      for el in sl:
        if el['name'] == name:
          continue
        name = el['name']
        result.append(el)
      print result
      

      【讨论】:

      • +1 这个答案是唯一不变形数据集的答案。看起来一致,如果 OP 需要,字典可以有额外的项目。
      • 这里使用object()的目的是什么?
      • @nomulous:它创建了一个在字典中不可能找到的新对象。 None(或与此相关的任何其他现有对象)可以(尽管在这种情况下不是)在数据中找到。
      • @Ignacio 太棒了!我刚刚输入了object() == object(),答案是False,真的很方便。谢谢!
      【解决方案5】:

      这是我能想到的最简单的方法:

      names = set(d['name'] for d in my_dicts)
      new_dicts = []
      for name in names:
          d = dict(name=name)
          d['score'] = max(d['score'] for d in my_dicts if d['name']==name)
          new_dicts.append(d)
      
      #new_dicts
      [{'score': 2, 'name': 'Baz'},
       {'score': 3, 'name': 'Foo'},
       {'score': 3, 'name': 'Bar'}]
      

      我个人不喜欢在问题太小时不导入模块。

      【讨论】:

        【解决方案6】:

        如果你还没有听说过 group by,这是很好的用法:

        from itertools import groupby
        
        data=[
            {'name': 'Foo', 'score': 1},
            {'name': 'Bar', 'score': 2},
            {'name': 'Foo', 'score': 3},
            {'name': 'Bar', 'score': 3},
            {'name': 'Foo', 'score': 2},
            {'name': 'Baz', 'score': 2},
            {'name': 'Baz', 'score': 1},
            {'name': 'Bar', 'score': 1}
        ]
        
        keyfunc=lambda d:d['name']
        data.sort(key=keyfunc)
        
        ans=[]
        for k, g in groupby(data, keyfunc):
            ans.append({k:max((d['score'] for d in g))})
        print ans
        
        >>>
        [{'Bar': 3}, {'Baz': 2}, {'Foo': 3}]
        

        【讨论】:

          【解决方案7】:

          我想我可以在这里提出一个单行:

          result = dict((x['name'],x) for x in sorted(data,key=lambda x: x['score'])).values()
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-10-31
            • 1970-01-01
            • 1970-01-01
            • 2021-05-09
            • 2016-12-24
            • 1970-01-01
            • 2010-11-18
            • 1970-01-01
            相关资源
            最近更新 更多