Skip to content

Instantly share code, notes, and snippets.

@kelvinst
Last active August 29, 2015 14:10
Show Gist options
  • Save kelvinst/826c4deb51d27422e7f9 to your computer and use it in GitHub Desktop.
Save kelvinst/826c4deb51d27422e7f9 to your computer and use it in GitHub Desktop.
Ruby magics: `Enumerable#inject`

Ruby magics: Enumerable#inject

Como muitos já sabem, sou apaixonado pro ruby e já compartilhei vários motivos do por quê. Mas dentre os motivos que dei, não entrei muito em detalhes de códigos no ruby que me deixam sem ar de tão legais e fáceis de implementar. E um exemplo desse tipo de código é o método #inject que vou explicar pra vocês agora. Espero que se empolguem e que usem ele sabiamente.

O que ele faz?

Bom, pra quem caiu de paraquedas aqui: o module Enumerable é o modules que representa uma collection no ruby. Se você tem uma classe que é uma collection, apenas inclua esse module e implemente o método #each e você terá vários métodos úteis na sua collection.

E um desses métodos é o #inject, ou #reduce que é apenas um alias para #inject. Agora vamos aos detalhes desse método tão maravilhoso.

O conceito em si é muito simples, é apenas uma maneira de combinar todos os elementos da collection em um novo objeto. Ou seja, o método retorna um novo objeto a partir da iteração em todos os elementos.

Mas como isso?

E para implementar ele fazemos assim:

(1..3).inject(0) do |result, i|
  result + i
end
# => 6

Este método acima, soma todos os elementos do range 1..3. Esse mesmo código pode ser escrito assim: (1..3).inject(0, :+). Mas para explicar como o método funciona, vamos ficar com o primeiro exemplo. Como podem ver, só batendo o olho não é muito claro como ele funciona.

Então vou explicar como funciona: o método #inject itera no Enumerable e executa o bloco de código para cada iteração, igual ao #each. Mas o que é esse primeiro parêmtro result do bloco?

Então, esse é mais complicado de explicar 😁: no primeiro parâmetro do bloco o #inject passa para você o retorno da execução da iteração anterior, e na primeira iteração, ele é igual ao valor passado por parâmetro para o #inject, no nosso caso o 0.

Sacou? No nosso código por exemplo, na primeira iteração o result é 0, pois foi o que passamos por parâmetro. Na segunda iteração é 1, pois somamos o result que era 0, com o item da iteração i, que era 1, e assim por diante.

O resultado retornado pelo #inject é o retorno da última iteração.

Meio complicado só pra somar um array né? Mas lembre-se que você pode fazer isso em uma linha de código, e ainda deixar o código bem legível! Olha outra utilidade para essa "belezura":

(1..3).inject(Hash.new) do |result, i|
  result[i.to_s] = i
  result
end
# => {'1' => 1, ... }

Viu? Facilmente convertemos um range para um hash! Consegue perceber o poder que isso te dá? Então vá e use ele!

Conclusão

Lembre-se, com grandes poderes vêm grandes responsabilidades. Use o inject com conciência, por mais magnífico que ele seja, muitas vezes ele pode ficar bem ilegível, e bem difícil de dar manutenção.

Pra finalizar, uma dica: explore mais os métodos do module Enumerable, tem outros métodos muito úteis. 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment