Ordenar lista de dicionarios pelos valores das chaves. (Python)

De tempos em tempos nos deparamos com uma lista de objetos que precisam ser organizadas de acordo com um determinado valor. Neste post mostro como organizar uma lista de dicionarios (dict) utilizando uma ou multiplas chaves.

Um lembrete: Se você for ordenar uma lista de dicionários, certifique-se de que todos eles possuam as mesmas propriedades.

Para contexto, a lista que vamos utilizar neste exemplo é a seguinte:

unsorted_list = [{"id": 0, "name": "User", "age": 30},
    {"id": 1, "name": "Another User", "age": 42},
    {"id": 2, "name": "Another User", "age": 25},
    {"id": 3, "name": "Yet Another User", "age": 30}]

 

 

Outro aviso: Neste post vou utilizar a função pprint para exibir os resultados. Para quem não conhece, esta função é muito boa e serve para mostrar dados no console de forma mais legível.

Para utiliza-la, basta incluir este import:

from pprint import pprint

Agora é só utilizar o comando pprint ao invés do print padrão.

Sem maiores delongas, vamos as ordenações:

 

Ordenando a lista por uma chave (e explicando a mecânica básica do processo)

Vamos começar pela forma mais simples: Ordenar uma lista de dicionários pelo valor de uma chave.

A chave que queremos utilizar nesta ordenação é: name, ou seja, quero que a lista de dicionários seja ordenada pelo nome do usuário.

sorted_list = sorted(unsorted_list, key=lambda k: k['name']) 
pprint(sorted_list)

Para esta ordenação, utilizamos a função sorted, passando como primeiro parâmetro a nossa lista desordenada (unsorted_list) e no parametro key, utilizamos uma expressão lambda. O resultado obtido foi:

[{'age': 42, 'id': 1, 'name': 'Another User'},
 {'age': 25, 'id': 2, 'name': 'Another User'},
 {'age': 30, 'id': 0, 'name': 'User'},
 {'age': 30, 'id': 3, 'name': 'Yet Another User'}]

 

Explicando um pouco do que está acontecendo:

Sobre a função sorted: Ela vai comparar item a item da sua lista. Se você estivesse comparando uma lista de números, de strings ou de qualquer coisa que possa ser comparada utilizando < (menor que), não seria necessário passar outro parâmetro. Ela já ordenaria naturalmente. Todavia, estamos utilizando dicionários e eles não podem ser ordenados sem uma diretriz (ou chave) como parametro.

Se você tentar, será agraciado com este erro:

TypeError: '<' not supported between instances of 'dict' and 'dict'

Isso acontece por que a função sorted tenta ordenar sua lista de dicionários, mas não sabe qual propriedade deve utilizar para fazer isso. O que faz todo sentido. Se alguém pedir para você ordenar um conjunto de livros, você vai precisar saber se eles devem ser ordenados por titulo, autor, categoria, ano de lançamento, etc…

Para resolver este problema, utilizamos o parámetro key. Só que ele não é um parâmetro comum. Ele não recebe uma string o um número. Este argumento recebe um objeto que seja callable, ou seja, um função. (Existe uma definição um pouco mais ampla para o conceito de callable, mas não é o foco do post e função é callable, então já resolve. 🙂 )

Neste momento, entra em ação a expressão lambda, que serve para gerar uma função anonima no fonte. Neste caso, a função serve apenas para para retornar o valor de cada dicionário que você quer.

As expressões lambda são muito flexíveis e existem muitas possibilidades, mas a que estamos utilizando funciona da seguinte forma:

  1. Recebe um argumento chamado k;
  2. Retorna o valor de k[‘name’]

 

Para demonstrar o funcionamento desta expressão lambda, vamos pegar um dos itens da lista e jogar direto na lambda para verificarmos o que acontece:

>>> user_name = (lambda k: k['name'])(unsorted_list[0])
>>> print(user_name)
User

No código acima, peguei o primeiro elemento da lista e joguei para a expressão lambda. O valor retornado foi User, que é o nome do primeiro usuário da lista.

Caso ainda não esteja claro, veja o exemplo abaixo:

def get_name(k):
    return k["name"]

user_name = get_name(unsorted_list[0])
print(user_name)

Neste exemplo, defini a função get_name, que recebe um argumento (k) e retorna a propriedade ‘name’ deste objeto. Este exemplo é a conversão da expressão lambda para uma ‘função normal’.

 

Se não quiser utilizar a expressão lambda, você pode usar a função itemgetter do pacote operator:

from operator import itemgetter

sorted_list = sorted(unsorted_list, key=itemgetter('name')) 
pprint(sorted_list)

O resultado final será o mesmo.

 

Agora que já entendemos como funciona a ordenação por uma chave…

 

Ordenando a lista por duas chaves

A ideia desta ordenação segue a lógica da anterior. Neste caso, vamos ordenar por nome e idade (propriedades: name e age).

sorted_list = sorted(unsorted_list, key=lambda k: (k['name'], k['age'])) 
pprint(sorted_list)

Com esta ordenação, vamos obter o seguinte resultado:

[{'age': 25, 'id': 2, 'name': 'Another User'},
 {'age': 42, 'id': 1, 'name': 'Another User'},
 {'age': 30, 'id': 0, 'name': 'User'},
 {'age': 30, 'id': 3, 'name': 'Yet Another User'}]

 

Veja que agora, quando dois usuários possuem o mesmo nome, os mais novos (com menor idade) aparecem antes.

A lógica desta ordenação foi a mesma, mas passamos a retornar um tuple com valores de nome e idade ao invés de retornar apenas o nome.

Veja abaixo o que esta lambda retorna:

>>> user_name_age = (lambda k: (k['name'], k['age']))(unsorted_list[0])
>>> print(user_name_age)
('User', 30)

O tuple retornado pode ser comparado utilizando o < (menor que). Veja:

>>> print(('User', 30) < ('User', 15))
False

>>> print(('User', 30) > ('User', 15))
True

 

 

Extra: Invertendo a ordenação.

A função sorted ainda aceita um terceiro parâmetro, que é o reverse. Se você passar o valor True neste argumento, sua lista virá em ordem decrescente. Funciona tanto para uma quanto para duas chaves de ordenação.

 

 

Espero ter ajudado.

The following two tabs change content below.
Arquiteto de Software e Desenvolvedor Backend (quase Fullstack), geralmente trabalho com C#, PowerShell, Python, Golang, bash e Unity (esse é mais por hobby). Estou sempre buscando algo novo para aprender, adicionando novas ferramentas ao meu cinto de utilidades.
Posted in Dev, Python and tagged , , , , , .