【问题标题】:Filter latest items in a list过滤列表中的最新项目
【发布时间】:2013-12-30 17:01:44
【问题描述】:

我在 Python 中有这么大的数据结构——基本上是一个字典列表。这些字典中的每一个都可能包含一些重复出现的属性和时间戳。我正在尝试查看这些属性的值是否相同,如果是,则仅获取具有最新时间戳的字典。例如:

[{'data': '3.50.1', 'date_time': '20131213-100308', 'version': '8.0.22'},
 {'data': '3.50.1', 'date_time': '20131230-100308', 'version': '8.0.22'},
 {'data': '3.47.0', 'date_time': '20131213-150410', 'version': '8.0.21'}]

字典包含更多字段,但假设这些是可能重复出现的常见字段。在这种情况下,上面的列表应该被过滤到只有两个字典——第二个和第三个。有什么聪明的方法可以在不进行双 for 循环的情况下实现这一点?

我尝试使用 lambda 函数并使用 Python 的过滤器,但无济于事。

【问题讨论】:

  • 列表是否按重复值总是在相邻项中的意义排序?
  • 每个dict 都将拥有相同的密钥,还是有些拥有更多、更少或不同的密钥?另外,您说您想要相同“数据”的最新,但这里的第一个dict 有更早的日期?
  • 列夫,不一定。它们按特定顺序插入,但我不确定 JSON 是如何决定存储它的。
  • tjiko,每个字典都有相同的键。对不起,我的意思是第二个字典而不是第一个。我纠正了错字。

标签: python list dictionary lambda


【解决方案1】:

如果记录已经“分组”在一起,即要从中选择的记录是相邻的,您可以简单地使用itertools.groupbymax()key = lambda rec: rec['date_time'] 来选择每个组中最近的(注意时间戳表示为字符串的方式,它们可以方便地按字典顺序进行比较):

from itertools import groupby
recs = [{'data': '3.50.1', 'date_time': '20131213-100308', 'version': '8.0.22'},
        {'data': '3.50.1', 'date_time': '20131230-100308', 'version': '8.0.22'},
   {'data': '3.47.0', 'date_time': '20131213-150410', 'version': '8.0.21'}]

filtered_recs = []
for key, group_iter in groupby(recs, lambda rec: rec['data']):
    recent_rec = max(group_iter, key = lambda rec: rec['date_time'])
    filtered_recs.append(recent_rec)

filtered_recs
=> 
[{'data': '3.50.1', 'date_time': '20131230-100308', 'version': '8.0.22'},
 {'data': '3.47.0', 'date_time': '20131213-150410', 'version': '8.0.21'}]

如果它们尚未组合在一起,您可以先对它们进行排序(可能效率低下),例如:

recs.sort(key=lambda rec: rec['data'])

或者,在上述解决方案中使用这个替代的、非 itertools 的 groupby 替代 itertools.groupby

def groupby(seq, func):
    groups = {}
    for x in seq:
        y = func(x)
        groups.setdefault(y, []).append(x)
    return groups

如果您要求“数据”和“版本”字段应相同,请将对 groupby 的调用更改为:groupby(recs, lambda rec: (rec['data'], rec['version']) ):

【讨论】:

  • 到目前为止似乎工作得很好。谢谢。但我不太清楚你所说的“如果它们还没有组合在一起,你可以对它们进行排序”是什么意思。这不是您在解决方案中所做的吗?在这里排序有什么影响?
  • great :) 说你交换了第二条和第三条记录,所以你的集合没有“分组”。在第一个 sn-p 中,我不对它们进行排序,并且它不起作用(由于 itertools.groupby 的行为方式)。第二个 sn-p(sort 行)是在调用groupby 之前调用的。通过排序,您可以确保您的记录被“分组”。那么它应该可以工作了。
  • 哦,我明白了。在那种情况下,我最好先对它们进行排序:)。出于某种原因,这很奇怪,即使没有先对其进行排序,它似乎也能正常工作。也许有缺陷,但我还没有仔细看。顺便说一句,在您的替代解决方案中,“func”是什么?
  • itertools.groupby 传递的内容相同,即lambda rec: rec['data']
【解决方案2】:

试试这样的

def findLatestDict(lst):
    latestDict = lst[0]
    latestTime = latestDict["date_time"]

    sameTimeList = []

    for aDict in lst:
        if aDict["date_time"] > latestTime:
            latestTime = aDict["date_time"]
            latestDict = aDict
            sameTimeList = []
        elif aDict["date_time"] == latestTime:
            sameTimeList.append(aDict)

    return (latestDict, sameTimeList)

此函数将返回它找到的第一个带有最新时间戳的字典,以及具有相同时间戳的所有其他字典的列表。

【讨论】:

  • 当我只想比较具有一组常见键值对的字典的时间戳时,比较列表中每个字典的时间戳对我没有多大用处。
  • 然后你可以把那个有特定键值对的子列表传递给这个函数
  • 好吧,弄清楚那个子列表是问题的一部分。
  • @rexbelia 你可以通过管道理解很容易地做到这一点:{key: aDict[key] for key in aDict.keys() if aDict[key] == val}
  • 知道了,我可以根据您的需要更新我的答案,但似乎 itertools 的答案非常适合您
【解决方案3】:

如果数据正确排序,最好使用 itertools.groupby。
如果数据没有排序,你可以这样做:

data = [
    {'data': '3.50.1', 'date_time': '20131213-100308', 'version': '8.0.22'},
    {'data': '3.50.1', 'date_time': '20131230-100308', 'version': '8.0.22'},
    {'data': '3.47.0', 'date_time': '20131213-150410', 'version': '8.0.21'},
]

def filtered(data):
    temp = dict()
    for row in data:

        # decorate
        stamp = row.pop('date_time')
        key = tuple(sorted(row.items()))

        # filter
        if temp.get(key, '')<stamp:
            temp[key] = stamp

    # undecorate
    for key, stamp in temp.items():
        d = dict(key)
        d['date_time'] = stamp
        yield d

for row in filtered(data):
    print row

【讨论】:

    猜你喜欢
    • 2020-07-08
    • 1970-01-01
    • 2013-01-29
    • 1970-01-01
    • 2011-07-25
    • 1970-01-01
    • 2021-02-23
    • 2017-08-20
    • 2021-02-28
    相关资源
    最近更新 更多