Supposons que j'ai une liste de dictées comme celle-ci:
list = [{'key':1,'timestamp':3456789012,'action':'like','type':'photo','id':212}`
{'key':2,'timestamp':2345678901,'action':'like','type':'photo','id':252}]
Je souhaite unifier la liste des dictées en fonction de la clé et de l ' horodatage .
Plus précisément, je souhaite conserver les dictionnaires avec une clé unique et conserver le horodatage le plus récent lorsqu'il y a des clés en double basé sur key.
Par conséquent, je veux avoir ce qui suit:
list = [{'key':1,'timestamp':1234567890,'action':'like','type':'photo','id':245},
{'key':2,'timestamp':2345678901,'action':'like','type':'photo','id':252},
{'key':1,'timestamp':3456789012,'action':'like','type':'photo','id':212}]
Comment puis-je faire efficacement ceci?
5 Réponses :
from itertools import groupby
import itertools
from operator import itemgetter
from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()
@b.add_function()
def kederrac(lst):
r = {}
for d in lst:
k = d['key']
if k not in r or r[k]['timestamp'] < d['timestamp']:
r[k] = d
return list(r.values())
@b.add_function()
def Daweo(lst):
s = sorted(lst, key=lambda x:(x['key'],x['timestamp']), reverse=True)
return [next(g) for k, g in itertools.groupby(s, lambda x:x['key'])]
@b.add_function()
def Jan(lst):
result = []
sorted_lst = sorted(lst, key=lambda x: x['key'])
for k,v in groupby(sorted_lst, key = lambda x: x['key']):
result.append(max(v, key=lambda x: x['timestamp']))
return result
@b.add_function()
def Jan_one_line(lst):
keyfunc = lambda x: x['key']
return [max(v, key = lambda x: x['timestamp'])
for k, v in groupby(sorted(lst, key=keyfunc), key=keyfunc)]
@b.add_function()
def gold_cy(lst):
key = itemgetter('key')
ts = itemgetter('timestamp')
def custom_sort(item):
return (key(item), -ts(item))
results = []
for k, v in groupby(sorted(lst, key=custom_sort), key=key):
results.append(next(v))
return results
@b.add_arguments('Number of dictionaries in list')
def argument_provider():
for exp in range(2, 18):
size = 2**exp
yield size, [{'key':choice(range((size // 10) or 2)),
'timestamp': randint(1_000_000_000, 10_000_000_000),
'action':'like','type':'photo','id':randint(100, 10000)}
for _ in range(size)]
r = b.run()
r.plot()
Je suppose qu'il n'y a pas besoin de trier ici. Bonne réponse. +1
Nous pouvons utiliser une combinaison de itertools.groupby et itemgetter . Une mise en garde est que les données doivent être pré-triées pour que itertools.groupby fonctionne correctement.
from itertools import groupby
from operator import itemgetter
key = itemgetter('key')
ts = itemgetter('timestamp')
def custom_sort(item):
return (key(item), -ts(item))
results = []
for k, v in groupby(sorted(data, key=custom_sort), key=key):
results.append(next(v))
[{'id': 212,
'action': 'like',
'key': 1,
'timestamp': 3456789012,
'type': 'photo'},
{'id': 252,
'action': 'like',
'key': 2,
'timestamp': 2345678901,
'type': 'photo'}]
En remarque, ne nommez pas variable utilisant des noms intégrés comme list ou id.
Le moyen le plus simple serait de l'insérer dans un dict, puis de relire toutes les valeurs sous forme de liste. Vous ne devez pas non plus utiliser list comme nom d'une variable.
d = {}
for item in lst:
key = item['key']
if key not in d or item['timestamp'] > d[key]['timestamp']:
d[key] = item
list(s.values())
Vous pouvez le faire en utilisant itertools.group de la manière suivante:
[{'key': 2, 'timestamp': 2345678901, 'action': 'like', 'type': 'photo', 'id': 252}, {'key': 1, 'timestamp': 3456789012, 'action': 'like', 'type': 'photo', 'id': 212}]
Sortie:
import itertools
lst = [{'key':1,'timestamp':1234567890,'action':'like','type':'photo','id':245},{'key':2,'timestamp':2345678901,'action':'like','type':'photo','id':252},{'key':1,'timestamp':3456789012,'action':'like','type':'photo','id':212}]
s = sorted(lst, key=lambda x:(x['key'],x['timestamp']), reverse=True)
uniq_lst = [next(g) for k, g in itertools.groupby(s, lambda x:x['key'])]
Premièrement, je trie par clé, horodatage donc les éléments avec la même clé seront adjacents et inversés, donc l'horodatage le plus élevé sera en premier. Ensuite, je regroupe les éléments avec la même clé et j'obtiens le premier enregistrement de chaque groupe.
Une autre solution avec itertools.groupby :
keyfunc = lambda x: x['key']
result = [max(v, key = lambda x: x['timestamp'])
for k, v in groupby(sorted(lst, key=keyfunc), key=keyfunc)]
Ou - si vous êtes dans les one-liners:
from itertools import groupby
lst = [{'key':1,'timestamp':1234567890,'action':'like','type':'photo','id':245},
{'key':2,'timestamp':2345678901,'action':'like','type':'photo','id':252},
{'key':1,'timestamp':3456789012,'action':'like','type':'photo','id':212}]
result = []
sorted_lst = sorted(lst, key=lambda x: x['key'])
for k,v in groupby(sorted_lst, key = lambda x: x['key']):
result.append(max(v, key=lambda x: x['timestamp']))
print(result)
De plus, ne nommez pas vos variables comme des fonctions intégrées, par exemple liste ou id . id (...) renvoie l ' identité d'un objet (aléatoire, mais unique dans le même programme).
Merci, fonctionne probablement. Corrigez simplement votre clé id .
id: 245 devrait être "id": 245 etc si je ne me trompe pas :)