web-dev-qa-db-fra.com

Trier le dictionnaire imbriqué par valeur et le reste par une autre valeur, dans Python

Considérez ce format de dictionnaire.

{'KEY1':{'name':'google','date':20100701,'downloads':0},
 'KEY2':{'name':'chrome','date':20071010,'downloads':0},
 'KEY3':{'name':'python','date':20100710,'downloads':100}}

J'aimerais que le dictionnaire soit d'abord trié par téléchargements, puis tous les éléments sans téléchargement triés par date. Évidemment, un dictionnaire ne peut pas être trié, j'ai juste besoin d'une liste triée de clés que je peux parcourir.

['KEY3','KEY1','KEY2']

Je peux déjà trier la liste par l'une ou l'autre des valeurs à l'aide de sorted, mais comment puis-je également trier par seconde valeur?

33
user479870

Utilisez l'argument key pour sorted(). Il vous permet de spécifier une fonction qui, étant donné l'élément réel en cours de tri, renvoie une valeur qui doit être triée. Si cette valeur est un Tuple, elle trie comme le tri des tuples - par la première valeur, puis par la deuxième valeur.

sorted(your_list, key=lambda x: (your_dict[x]['downloads'], your_dict[x]['date']))
42
Amber

Vous pouvez passer une fonction key à sorted qui retourne un Tuple contenant les deux choses que vous souhaitez trier. En supposant que votre gros dictionnaire s'appelle d:

def keyfunc(tup):
    key, d = tup
    return d["downloads"], d["date"]

items = sorted(d.items(), key = keyfunc)

Vous pouvez le faire avec un lambda si vous préférez, mais c'est probablement plus clair. Voici le code équivalent basé sur lambda:

items = sorted(d.items(), key = lambda tup: (tup[1]["downloads"], tup[1]["date"]))

Soit dit en passant, puisque vous avez mentionné que vous vouliez d'abord trier par "téléchargements", les deux exemples ci-dessus trient en fonction du nombre de téléchargements dans l'ordre croissant. Cependant, du contexte, il semble que vous souhaitiez trier par ordre décroissant de téléchargements, auquel cas vous diriez

return -d["downloads"], d["date"]

dans votre keyfunc. Si vous vouliez quelque chose comme trier par ordre croissant pour les numéros de téléchargement différents de zéro, puis avoir tous les enregistrements de téléchargement nul après cela, vous pourriez dire quelque chose comme

return (-d["downloads"] or sys.maxint), d["date"]
9
Eli Courtwright

Mon autre réponse était fausse (comme la plupart des réponses ici)

sorted_keys = sorted((key for key in outer_dict if outer_dict[key]['downloads']),
                     key=lambda x: (outer_dict[key]['downloads'],
                                    outer_dict[key]['downloads'])
                     reverse=True)

sorted_keys += sorted((key for key in outer_dict if not outer_dict[key]['downloads']),
                      key=lambda x: outer_dict[key]['date'])

Cela créera une liste avec les éléments qui ont été téléchargés triés en ordre décroissant à l'avant et les autres éléments qui n'ont pas été téléchargés triés par date après ceux qui l'ont.

Mais en fait, la dernière partie de réponse d'Eli Courtwrights est la meilleure.

2
aaronasterling
a = {'KEY1':{'name':'google','date':20100701,'downloads':0},
 'KEY2':{'name':'chrome','date':20071010,'downloads':0},
 'KEY3':{'name':'python','date':20100710,'downloads':100}}


z = a.items()

z.sort(key=lambda x: (x[1]['downloads'], x[1]['date']))
0
mouad