【问题标题】:Python filtering list of objects by distinct attributePython按不同属性过滤对象列表
【发布时间】:2017-07-31 20:19:28
【问题描述】:

我有一个具有多个属性的对象列表。我想根据对象的一个​​属性(country_code)过滤列表,即

当前列表

elems = [{'region_code': 'EUD', 'country_code': 'ROM', 'country_desc': 'Romania', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'ROM', 'country_desc':'Romania', 'event_number': '3200'}, 
{'region_code': 'EUD', 'country_code': 'ROM', 'country_desc': 'Romania', 'event_number': '4000'}, 
{'region_code': 'EUD', 'country_code': 'SVN', 'country_desc': 'Slovenia', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'NLD', 'country_desc':'Netherlands', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'BEL', 'country_desc':'Belgium', 'event_number': '6880'}]

想要的列表

elems = [{'region_code': 'EUD', 'country_code': 'ROM', 'country_desc': 'Romania', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'SVN', 'country_desc': 'Slovenia', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'NLD', 'country_desc': 'Netherlands', 'event_number': '6880'}, 
{'region_code': 'EUD', 'country_code': 'BEL', 'country_desc': 'Belgium', 'event_number': '6880'}]

我可以通过创建字典和 for 循环来实现这一点,但我觉得在 python 中使用 filter() 或 reduce() 函数有更简单的方法,我只是不知道如何。

任何人都可以使用内置的 python 函数来简化下面的代码吗?性能是一个重要因素,因为真实数据将是大量的。

工作代码:

unique = {}
for elem in elems:
  if elem['country_code'] not in unique.keys():
     unique[elem['country_code']] = elem

print(unique.values())

值得注意的是,我也尝试了下面的代码,但它的性能比当前的工作代码差:

unique = []
for elem in elems:
    if not any(u['country_code'] == elem['country_code'] for u in unique):
        unique.append(elem)

【问题讨论】:

  • 我认为您的第一个工作代码(带有 dict)非常理想。也许使用collections.OrderedDict 这样顺序与列表中的相同,如果这很重要的话。此外,无需致电.keys()

标签: python dictionary filtering


【解决方案1】:

我认为您的第一种方法已经非常接近最优。字典查找速度很快(与 set 中的一样快)并且循环很容易理解,尽管有点冗长(按照 Python 标准),但您不应该为了简洁而牺牲可读性。

但是,您可以使用setdefault 减少一行,并且您可能希望使用collections.OrderedDict() 以便结果列表中的元素按其原始顺序排列。另请注意,在 Python 3 中,unique.values() 不是列表,而是字典上的视图。

unique = collections.OrderedDict()
for elem in elems:
    unique.setdefault(elem["country_code"], elem)

如果你真的,真的想要使用reduce,你可以使用空dict作为初始化器,然后使用d.setdefault(k,v) and d设置值(如果不存在)并返回修改后的值字典。

unique = reduce(lambda unique, elem: unique.setdefault(elem["country_code"], elem) and unique,
                elems, collections.OrderedDict())

不过,我只会使用循环。

【讨论】:

  • setdefault 我这辈子都去哪儿了??这很好用,谢谢!
【解决方案2】:

我认为你的方法很好。检查elem['country_code'] not in unique而不是elem['country_code'] not in unique.keys()会稍微好一些。

但是,这里有另一种使用列表理解的方法:

visited = set()
res = [e for e in elems
        if e['country_code'] not in visited
        and not visited.add(e['country_code'])]

最后一点滥用了not None == Truelist.add 返回None 的事实。

【讨论】:

  • 感谢@Yakym - 这又快又干净。我将使用 tobias_k 的答案,因为它的代码更改较少,但性能是相同的。谢谢!
猜你喜欢
  • 2020-07-01
  • 2014-11-24
  • 1970-01-01
  • 2019-06-08
  • 2020-12-14
  • 2017-09-22
  • 2022-11-15
  • 2013-06-01
  • 2012-08-11
相关资源
最近更新 更多