【问题标题】:create unique list: comparing dict objects创建唯一列表:比较 dict 对象
【发布时间】:2017-03-12 09:36:48
【问题描述】:

我有一个对象列表:带有 id、日期和对象类型的指示。例如

original_list = [{'id':1,'date':'2016-01-01','type':'A'},
                 {'id':2,'date':'2016-02-01','type':'B'},
                 {'id':3,'date':'2016-03-01','type':'A'},
                 {'id':1,'date':'2016-04-01','type':'C'}]

如上所示,此列表可以包含重复的 ID 和不同的日期、类型。现在我想创建一个唯一 ID 列表,其中仅包含最后一个条目(基于日期)。现在我有一个程序如下:

# Create list of unique id's
unique_ids = list(set([foo.get('id') for foo in original_list]))

# find last contact
for unique_id in unique_ids:
    foo_same_id = [foo for foo in original_list if foo.get('id') == unique_id]
    if len(foo_same_id) == 1:
        # use this one
    else:
        latest_date = [foo.get('date') for foo in foo_same_id]
        latest_date = max(latest_date)
        latest_object = [foo for foo in foo_same_id if foo.get('date') == latest_date]

在此之后,具有相同 id 的列表按日期排序,并且是用于填写对象类型的 type 的最后一个值。那时我不再需要这些对象,而是复制两个列表(original_list 和 unique_ids)而没有处理过的对象/id。

这似乎可行,但是当应用于 200.000 + 时,它需要很长时间(+ 4 小时)。有没有办法加快这个速度?不同的实现?目前我正在从数据库中读取数据并立即开始处理。

【问题讨论】:

  • 显然你应该在数据库级别进行排序!!!
  • 是否建议将数据插入到临时表中对该表执行这些操作,然后只读取相关数据?目前不可能在原始查询中执行这些操作。
  • 停止一切并阅读有关 WHERE 和 ORDER By 的信息
  • @M.Doe 你能分享你的数据库表的结构或你用来获取这些数据的查询吗?

标签: python python-3.x date dictionary


【解决方案1】:

而不是使用 set 和其他额外操作创建所有唯一 ID,然后遍历列表并使用所有这些额外操作 操作,您可以简单地使用自定义字典,以便根据其 id 保留您的字典。而且由于字典只保留 如果您以仅根据日期替换值的方式覆盖__setitem__ 方法(如果它大于当前值),则为唯一项 您只需创建您的愿望清单。

from datetime import datetime


class UniqueDict(dict):
    def __init__(self, *args, **kwds):
        super(UniqueDict, self).__init__(*args, **kwds)

    def __setitem__(self, _id, value):
        current = self.get(_id)
        if current:
            date_obj = datetime.strptime(value['date'], '%Y-%m-%d')
            current_date_obj = datetime.strptime(self[_id]['date'], '%Y-%m-%d')
            if date_obj > current_date_obj:
                dict.__setitem__(self, _id, value)
        else:
            dict.__setitem__(self, _id, value)

演示:

original_list = [{'id':1,'date':'2016-01-01','type':'A'},
                 {'id':2,'date':'2016-02-01','type':'B'},
                 {'id':3,'date':'2016-03-01','type':'A'},
                 {'id':1,'date':'2016-04-01','type':'C'}]


udict = UniqueDict()

for d in original_list:
    udict[d['id']] = d

print(udict)

输出:

{1: {'id': 1, 'date': '2016-04-01', 'type': 'C'},
 2: {'id': 2, 'date': '2016-02-01', 'type': 'B'},
 3: {'id': 3, 'date': '2016-03-01', 'type': 'A'}}

注意,如评论中所述,在这种情况下,您也可以放弃使用 datetime 将日期字符串转换为日期对象进行比较,因为 ISO 格式的日期可以按字典顺序进行比较。

【讨论】:

  • 您可以在这种情况下删除日期时间转换,因为可以按字典顺序比较 ISO 8601 格式。
  • @AshwiniChaudhary 确实如此。实际上我使用了datetime,因为它是一种通用且最合适的方式,应该用于其他日期格式。
  • @JonClements 在这种情况下,是的,这肯定是矫枉过正。
【解决方案2】:

使用仅遍历列表一次并在最后将其展平的自定义函数来删除原始数据:

def dedup_original(original):
    items = {}
    for item in original:
        if item['id'] in items:
            if items[item['id']]['date'] < item['date']:
                items[item['id']] = item
        else:
             items[item['id']] = item
    return list(items.values())

结果:

In [28]: dedup_original(original_list)
Out[28]:
[{'date': '2016-04-01', 'id': 1, 'type': 'C'},
 {'date': '2016-02-01', 'id': 2, 'type': 'B'},
 {'date': '2016-03-01', 'id': 3, 'type': 'A'}]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-19
    • 2022-06-26
    相关资源
    最近更新 更多