Métodos permutation e combination no Ruby

Dicas - 07/Mar/2021 - por Henrique Morato

O Ruby tem dois métodos que, apesar de pouco usados no dia a dia, podem te ajudar a resolver alguns problemas.

Vamos supor que temos uma lista de nomes de cidades gravadas numa coleção:

cidades = [‘São Paulo’, ‘Brasília’, ‘Rio de Janeiro’, ‘Curitiba’]

Mas, infelizmente, só podemos visitar 3 delas, então seria interessante ver uma lista com todas as possibilidades de rotas disponíveis para termos uma ideia se aquela rota é interessante.

Para isso podemos usar o método permutation:

cidades.permutation(3).to_a
# => [["São Paulo", "Brasília", "Rio de Janeiro"], ["São Paulo", "Brasília", "Curitiba"], ["São Paulo", "Rio de Janeiro", "Brasília"], ["São Paulo", "Rio de Janeiro", "Curitiba"], ["São Paulo", "Curitiba", "Brasília"], ["São Paulo", "Curitiba", "Rio de Janeiro"], ["Brasília", "São Paulo", "Rio de Janeiro"], ["Brasília", "São Paulo", "Curitiba"], ["Brasília", "Rio de Janeiro", "São Paulo"], ["Brasília", "Rio de Janeiro", "Curitiba"], ["Brasília", "Curitiba", "São Paulo"], ["Brasília", "Curitiba", "Rio de Janeiro"], ["Rio de Janeiro", "São Paulo", "Brasília"], ["Rio de Janeiro", "São Paulo", "Curitiba"], ["Rio de Janeiro", "Brasília", "São Paulo"], ["Rio de Janeiro", "Brasília", "Curitiba"], ["Rio de Janeiro", "Curitiba", "São Paulo"], ["Rio de Janeiro", "Curitiba", "Brasília"], ["Curitiba", "São Paulo", "Brasília"], ["Curitiba", "São Paulo", "Rio de Janeiro"], ["Curitiba", "Brasília", "São Paulo"], ["Curitiba", "Brasília", "Rio de Janeiro"], ["Curitiba", "Rio de Janeiro", "São Paulo"], ["Curitiba", "Rio de Janeiro", "Brasília"]]

Pausa na historinha!

Antes de continuar, uma operação interessante para discutirmos é porque temos que chamar to_a para converter em array no final.

Essa chamada é necessária para ver o resultado, porque não temos nenhuma ação com os dados depois de transformá-los como um map ou each. Como o retorno do método permutation é um Enumerator e só queremos mostrar os valores, o to_a se torna necessário.

cidades.permutation(3)
# => #<Enumerator: ["São Paulo", "Brasília", "Rio de Janeiro", "Curitiba"]:permutation(3)>

Play na historinha!

Agora temos uma lista de cidades em todas as ordens possíveis. Por exemplo, a rota de São Paulo até Brasília e Rio de Janeiro aparece em diversas ordens:

[["São Paulo", "Brasília", "Rio de Janeiro"], ["São Paulo", "Rio de Janeiro", "Brasília"], ["Brasília", "São Paulo", "Rio de Janeiro"], ["Brasília", "Rio de Janeiro", "São Paulo"], ["Rio de Janeiro", "São Paulo", "Brasília"],  ["Rio de Janeiro", "Brasília", "São Paulo"]]

Isso pode ser um problema, você pode querer só olhar as 3 cidades e pensar se elas são legais, independente da ordem!

Para chegar nesse retorno, podemos usar o método combination:

cidades.combination(3).to_a
# => [["São Paulo", "Brasília", "Rio de Janeiro"], ["São Paulo", "Brasília", "Curitiba"], ["São Paulo", "Rio de Janeiro", "Curitiba"], ["Brasília", "Rio de Janeiro", "Curitiba"]]

Agora temos todas as combinações de cidades, mas sem nenhuma repetição entre elas como no exemplo anterior.

Além destes, na doc do Ruby também temos os métodos repeated_permutations e repeated_combinations, onde elementos podem se repetir. Vamos ver um exemplo usando repeated_combinations com as cidades acima:

cidades.repeated_combination(3).to_a
# => [["São Paulo", "São Paulo", "São Paulo"], ["São Paulo", "São Paulo", "Brasília"], ["São Paulo", "São Paulo", "Rio de Janeiro"], ["São Paulo", "São Paulo", "Curitiba"], ["São Paulo", "Brasília", "Brasília"], ["São Paulo", "Brasília", "Rio de Janeiro"], ["São Paulo", "Brasília", "Curitiba"], ["São Paulo", "Rio de Janeiro", "Rio de Janeiro"], ["São Paulo", "Rio de Janeiro", "Curitiba"], ["São Paulo", "Curitiba", "Curitiba"], ["Brasília", "Brasília", "Brasília"], ["Brasília", "Brasília", "Rio de Janeiro"], ["Brasília", "Brasília", "Curitiba"], ["Brasília", "Rio de Janeiro", "Rio de Janeiro"], ["Brasília", "Rio de Janeiro", "Curitiba"], ["Brasília", "Curitiba", "Curitiba"], ["Rio de Janeiro", "Rio de Janeiro", "Rio de Janeiro"], ["Rio de Janeiro", "Rio de Janeiro", "Curitiba"], ["Rio de Janeiro", "Curitiba", "Curitiba"], ["Curitiba", "Curitiba", "Curitiba"]]

Note que “São Paulo”, pode aparecer várias vezes, como no primeiro elemento onde aparece 3 vezes.

É isso! Temos então bons métodos que podem nos ajudar a analisar dados!

Foto de perfil do autor
Henrique Morato

Dev