Método filter_map no Ruby 2.7

Dicas - 19/Jul/2020 - por André Kanamura

Na versão 2.7 do Ruby foi incluído o método Enumerable#filter_map que, como o nome diz, funciona como uma mistura de filter com map. De acordo com a documentação oficial:

Returns a new array containing the truthy results (everything except false or nil) of running the block for every element in enum.

Em tradução livre: "retorna um array novo contendo os resultados truthy (exceto false ou nil) depois de rodar o bloco para cada elemento do enum". Vamos demonstrar como isso acontece com exemplos simples rodando no IRB:

  numbers = [2, 5, 6, 8, 9, 11]
  numbers.select { |x| x.odd? }.map { |x| x * 2 }
  # => [10, 18, 22]
  numbers.map { |x| x * 2 if x.odd? }.compact
  # => [10, 18, 22]

Se utilizarmos o filter_map, podemos obter o mesmo resultado:

  numbers = [2, 5, 6, 8, 9, 11]
  numbers.filter_map { |x| x * 2 if x.odd? }
  # => [10, 18, 22]

Agora vamos mostrar um exemplo de uso do método um pouco mais próximo da realidade. Vamos considerar que nossa aplicação possui uma classe que pode receber uma lista de e-mails e criar um User para cada um deles. No entanto, cada e-mail deve ser validado antes do usuário ser criado. Para isso, temos um método email_hashes que itera pela lista e cria um array de hashes com os e-mails válidos. Esse array depois é utilizado para criar os usuários no método call.

class UserGenerator
  EMAIL_REGEXP = URI::MailTo::EMAIL_REGEXP

  def initialize(emails)
    @emails = emails
  end

  def call
    email_hashes.map { |hash| User.create(hash) }
  end

  def email_hashes
    @emails.map do |email|
      next unless email =~ EMAIL_REGEXP

      { email: email } 
    end
  end.compact
end

Note como o compact é utilizado no final de email_hashes para eliminar os resultados nil desse processamento. Agora se implementarmos o método com filter_map, ele poderia ficar assim:

  def email_hashes
    @emails.filter_map do |email|
      next unless email =~ EMAIL_REGEXP

      { email: email } 
    end
  end

O método filter_map é bastante útil, pois oferece uma forma simples e direta para resolver o problema de mapear somente elementos específicos de um array.

Referências

Foto de perfil do autor
André Kanamura

Dev na Campus Code