Ordenando dicionário por valores

Posted on Sat 23 March 2019 in rivendell

dict

Problema

Dado um dicionário, eu quero ordenar seus elementos de acordo com seus valores, não por sua chave.

Por exemplo o dicionário {1: "b", 2: "a"} se torna {2: "a", 1: "b"} pois "a" < "b" .

Discussão

Executar a função sorted sobre um dicionário tem como resultado uma lista ordenada de suas chaves.

A explicação disso é que a função sorted recebe um objeto iterável, ou seja uma estrutura de dados container que pode ter seus elementos internos percorridos múltiplas vezes e retorna uma nova lista ordenada de forma ascendente.

O comportamento padrão de um dicionário quando percorrido é retornar os valores de suas chaves.

for value in {1: "b", 2: "a"}:
    print(value)

Porém dicionários possuem o método items que retorna uma estrutura percorrível, com o valor de suas chaves e seus valores associados.

for tupla_chave_valor in {1: "b", 2: "a"}:
    print(tupla_chave_valor)

Essa estrutura se assemelha a uma lista de tuplas, em que cada tupla possui dois valores, a chave e o valor associado aquela chave.

dict_items([(1, "b"), (2, "a")])

Porém ainda que eu tenha os dois valores, ainda tenho um problema que é "como ordenar essa lista?".

O método sorted, ordena os elementos de forma ascendente, e no nosso caso, quando eu comparo as tuplas, o primeiro valor é levado em conta e somente caso ocorra uma igualdade segundo valor será utilizado.

Porém o método aceita um parâmetro chamado key, que é uma função aplicada a cada elemento antes da comparação.

Se eu defino por exemplo a função len como key em um método sort, os elementos serão ordenados pelo tamanho.

>>> sorted(["12", "123", "1"], key=len)
["1", "12", "123"]

Para conseguir ordenar nossa lista de tuplas pelo segundo elemento vamos recorrer a uma função do módulo operator.

O método em questão será o itemgetter, ele recebe como parâmetro uma ou mais posições que serão retornadas em um iterável.

from operator import itemgetter
original = {1: 2, 2: 3, 3: 1}
print(sorted(original.items(), key=itemgetter(1))))

Estamos quase lá, mas agora temos uma lista, mas não um dicionário.

Temos uma nova lista de tuplas ordenadas [(3, 1), (1, 2), (2, 3)].

Podemos fazer a conversão desta lista em um dicionário dict([(3, 1), (1, 2), (2, 3)])

Conclusão

O código final se torna:

from operator import itemgetter
original = {1: 2, 2: 3, 3: 1}
ordered = dict(sorted(original.items(), key=itemgetter(1)))
print(ordered)

Lembrando que esta solução é somente válida para versões acima da 3.6, anteriores a isto devem utilizar OrderedDict no lugar de dict.

Então é isso pessoal!

Até a próxima!

[] "s