1
votes

Liste Uniqify de dictionnaires basée sur des clés spécifiques - Conserver des occurrences spécifiques en cas de doublons

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?


0 commentaires

5 Réponses :


2
votes
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()

1 commentaires

Je suppose qu'il n'y a pas besoin de trier ici. Bonne réponse. +1



0
votes

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.


0 commentaires

1
votes

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()) 


0 commentaires

0
votes

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.


0 commentaires

1
votes

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).


2 commentaires

Merci, fonctionne probablement. Corrigez simplement votre clé id .


id: 245 devrait être "id": 245 etc si je ne me trompe pas :)