在Python中过滤字典列表的更好方法

时间:2015-06-08 12:54:00

标签: python python-2.7

我有一个字典列表,其结构与此类似:

log = [{'user_id': 'id1', 'action': 'action1', 'timestamp': 'time1'},  
       {'user_id': 'id2', 'action': 'action2', 'timestamp': 'time2'},
       ...]

并按时间戳值排序。

我想删除由同一用户完成的顺序相同的操作,只留下第一个,例如如果我有以下清单:

log = [{'user_id': 'id1', 'action': 'action1', 'timestamp': 'time1'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time2'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time3'},
       {'user_id': 'id2', 'action': 'action2', 'timestamp': 'time4'},
       {'user_id': 'id3', 'action': 'action2', 'timestamp': 'time5'},
       {'user_id': 'id3', 'action': 'action2', 'timestamp': 'time6'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time7'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time8'}]

我想获得此列表:

log = [{'user_id': 'id1', 'action': 'action1', 'timestamp': 'time1'},
       {'user_id': 'id2', 'action': 'action2', 'timestamp': 'time4'},
       {'user_id': 'id3', 'action': 'action2', 'timestamp': 'time5'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time7'}]

目前我这样做:

def merge_actions(log):
    merged_log = []
    merged_log.append(log[0])
    for i in range(1, len(log)):
        if log[i]['user_id'] == log[i-1]['user_id']:
            if log[i]['action'] == log[i-1]['action']:
                continue
        merged_log.append(log[i])
    return merged_log

有更好的方法吗?

4 个答案:

答案 0 :(得分:6)

如果您使用itertools.groupby并按'user_id''action'分组,则可以抓取每个组中的第一个元素。

>>> [next(group) for key, group in itertools.groupby(log, key = lambda i: (i['user_id'], i['action']))]
[{'timestamp': 'time1', 'action': 'action1', 'user_id': 'id1'},
 {'timestamp': 'time4', 'action': 'action2', 'user_id': 'id2'},
 {'timestamp': 'time5', 'action': 'action2', 'user_id': 'id3'},
 {'timestamp': 'time7', 'action': 'action1', 'user_id': 'id1'}]

答案 1 :(得分:3)

使用itertools.groupby对同一用户的连续操作进行分组,然后获取每个组的第一个元素:

def merge_actions(log):
    return [next(group) for key, group in itertools.groupby(log, lambda l: (l['user_id'], l['action']))

答案 2 :(得分:2)

如果你要使用循环,你只需要跟踪你看到的最后一个键:

it = iter(log)
start = next(it)
od,prev = [start], start["user_id"]
for d in it:
    k = d["user_id"]
    if prev != k:
        od.append(d)
    prev = k

print(od)

[{'action': 'action1', 'timestamp': 'time1', 'user_id': 'id1'}, 
 {'action': 'action2', 'timestamp': 'time4', 'user_id': 'id2'}, 
{'action': 'action2', 'timestamp': 'time5', 'user_id': 'id3'}, 
{'action': 'action1', 'timestamp': 'time7', 'user_id': 'id1'}]

如果操作并非总是分组,请检查两个键:

it = iter(log)
start = next(it)
od, prev,act = [start], start["user_id"],start["action"]
for d in it:
    k1, k2 = d["user_id"], d["action"]
    if prev != k1 or k2 != act:
        od.append(d)
    prev, act = k1, k2

答案 3 :(得分:1)

以下是使用groupby的详细尝试:

from itertools import groupby
a = [{'user_id': 'id1', 'action': 'action1', 'timestamp': 'time1'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time2'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time3'},
       {'user_id': 'id2', 'action': 'action2', 'timestamp': 'time4'},
       {'user_id': 'id3', 'action': 'action2', 'timestamp': 'time5'},
       {'user_id': 'id3', 'action': 'action2', 'timestamp': 'time6'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time7'},
       {'user_id': 'id1', 'action': 'action1', 'timestamp': 'time8'}]
for u, grps in groupby(a, lambda d: d['user_id']):
    d_with_first_ts = sorted(grps, key = lambda user_dict: user_dict['timestamp'])[0]
    print('User: {}; Dict with first timestamp = {}'.format(u, d_with_first_ts))

您将获得以下结果:

User: id1; Dict with first timestamp = {'timestamp': 'time1', 'action': 'action1', 'user_id': 'id1'}
User: id2; Dict with first timestamp = {'timestamp': 'time4', 'action': 'action2', 'user_id': 'id2'}
User: id3; Dict with first timestamp = {'timestamp': 'time5', 'action': 'action2', 'user_id': 'id3'}
User: id1; Dict with first timestamp = {'timestamp': 'time7', 'action': 'action1', 'user_id': 'id1'}