O que o método "map" faz no Ruby?

250

Eu sou novo em programação. Alguém pode explicar o .mapque faria em:

params = (0...param_count).map
bigpotato
fonte
9
Faça uma pergunta de cada vez. mapé um método "funcional" comum encontrado em objetos enumeráveis ​​usados ​​para transformar os valores em uma sequência (com considerações especiais). ..e ...são maneiras de criar intervalos. Além disso, familiarize-se com o REPL, onde você pode experimentar você mesmo! :)
5
REPL para ruby ​​é irb, para Rails é trilhos c. O REPL permite testar o código diretamente no próprio shell de idioma.
22614 Gary Gary

Respostas:

431

O mapmétodo pega um objeto enumerável e um bloco e executa o bloco para cada elemento, produzindo cada valor retornado do bloco (o objeto original é inalterado, a menos que você use map!):

[1, 2, 3].map { |n| n * n } #=> [1, 4, 9]

Arraye Rangesão tipos enumeráveis. mapcom um bloco retorna uma matriz. map!muda a matriz original.

Onde isso é útil e qual a diferença entre map!e each? Aqui está um exemplo:

names = ['danil', 'edmund']

# here we map one array to another, convert each element by some rule
names.map! {|name| name.capitalize } # now names contains ['Danil', 'Edmund']

names.each { |name| puts name + ' is a programmer' } # here we just do something with each element

A saída:

Danil is a programmer
Edmund is a programmer
Danil Speransky
fonte
3
obrigado speransky pelo exemplo. então, como o .map é diferente de .each?
bigpotato 23/08/12
2
Ahhh, entendi. Então .map na verdade modifica a matriz enquanto .each apenas percorre a matriz para acessar os valores enquanto deixa a matriz original intocada?
bigpotato 23/08/12
24
É perigoso para os leitores casuais que a frase de abertura descreve mapcomo se fossemap!
kaleidic
12
para ver a diferença entre o mapa e cada um, abra uma janela do IRB e observe os resultados de y e z no seguinte código: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej 27/11
7
@Inquisitive: 'each' retorna a matriz que a chama (no exemplo [1,2,3]) quando um bloco é fornecido, 'map' retorna uma nova matriz preenchida com os valores calculados pelo bloco. Isso pode ajudar: defina a variável ary = [1,2,3] e verifique sua object_id. Em seguida, execute y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Agora verifique os object_id de y e z. y tem o mesmo object_id que ary (porque cada ary retornado), mas z tem um object_id diferente, porque o mapa retornou uma nova matriz.
Davej
66

map, junto com selecte eaché um dos cavalos de trabalho de Ruby no meu código.

Permite executar uma operação em cada um dos objetos da sua matriz e devolvê-los todos no mesmo local. Um exemplo seria incrementar uma matriz de números em um:

[1,2,3].map {|x| x + 1 }
#=> [2,3,4]

Se você pode executar um único método nos elementos da sua matriz, pode fazê-lo em um estilo abreviado, como segue:

  1. Para fazer isso com o exemplo acima, você teria que fazer algo assim

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Para usar mais simplesmente a técnica de atalho "e comercial", vamos usar um exemplo diferente:

    ["vanessa", "david", "thomas"].map(&:upcase)
    #=> ["VANESSA", "DAVID", "THOMAS"]

A transformação de dados no Ruby geralmente envolve uma cascata de mapoperações. Study map& select, eles são alguns dos métodos Ruby mais úteis na biblioteca principal. Eles são tão importantes quanto each.

( maptambém é um apelido para collect. Use o que for melhor para você conceitualmente.)

Informações mais úteis:

Se o objeto Enumerable em que você estiver executando eachou mapcontiver um conjunto de elementos Enumerable (hashes, matrizes), você poderá declarar cada um desses elementos dentro de seus tubos de bloco da seguinte maneira:

[["audi", "black", 2008], ["bmw", "red", 2014]].each do |make, color, year|
  puts "make: #{make}, color: #{color}, year: #{year}"
end
# Output:
# make: audi, color: black, year: 2008
# make: bmw, color: red, year: 2014

No caso de um Hash (também um Enumerableobjeto, um Hash é simplesmente uma matriz de tuplas com instruções especiais para o intérprete). O primeiro "parâmetro do tubo" é a chave, o segundo é o valor.

{:make => "audi", :color => "black", :year => 2008}.each do |k,v|
    puts "#{k} is #{v}"
end
#make is audi
#color is black
#year is 2008

Para responder à pergunta real:

Supondo que paramsseja um hash, essa seria a melhor maneira de mapear através dele: use dois parâmetros de bloco em vez de um para capturar o par de chave e valor para cada tupla interpretada no hash.

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3
boulder_ruby
fonte
Isso não funciona para mim no irb. Eu entro NoMethodError: private method 'plusone' called for 1:Fixnumno ruby ​​2 e 'número errado de args' no ruby ​​1.9 / 1.8. De qualquer forma, eu usei um lambda: plusone = ->(x) { x + 1 }seguida, retire o símbolo especificador: [1,2,3].map(&plusone).
tjmcewan
1
hmm sons como você declarou privatedentro da classe onde você colocar o seu método antes de você colocar o seu método
boulder_ruby
Sim, totalmente. Exceto que não era. . :( Primeiro de tudo o que estava em um script reta w / o as classes, em segundo lugar no IRB simples Aqui está a minha copiar / colar do seu código: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan
Sim, acabei de colocar um péssimo exemplo no meu código, desculpe. Experimente o código modificado. Ele funciona agora ...
boulder_ruby
1
@boulder_ruby existe uma maneira de fazer isso com um método normal - como em, não um método de classe?
Tekknolagi
6

Usando o ruby ​​2.4, você pode fazer o mesmo, usando transform_valuesesse recurso extraído dos trilhos para o ruby.

h = {a: 1, b: 2, c: 3}

h.transform_values { |v| v * 10 }
 #=> {a: 10, b: 20, c: 30}
tokhi
fonte
4

0..param_countsignifica "até e incluindo param_count". 0...param_countsignifica "até, mas não incluindo param_count".

Range#mapnão retorna um Enumerable, na verdade ele mapeia para uma matriz. É o mesmo que Range#to_a.

Pedro Nascimento
fonte
3

Ele "mapeia" uma função para cada item em um Enumerable- nesse caso, um intervalo. Portanto, chamaria o bloco passado uma vez para cada número inteiro de 0 a param_count(exclusivo - você está certo sobre os pontos) e retornaria uma matriz contendo cada valor de retorno.

Aqui está a documentação para Enumerable#map. Também possui um alias collect,.

Ry-
fonte
É estranho, mas Range#mapna verdade converte para uma matriz.
Pedro Nascimento
1
@PedroNascimento: Sim ... foi o que eu disse?
Ry-
Desculpe, eu não sabia que o mapa chamado por si só não retornava um Enumerable, como cada um. Eu pensei que sim.
Pedro Nascimento
2

O mapa é uma parte do módulo enumerável. Muito parecido com "collect". Por exemplo:

  Class Car

    attr_accessor :name, :model, :year

    Def initialize (make, model, year)
      @make, @model, @year = make, model, year
    end

  end

  list = []
  list << Car.new("Honda", "Accord", 2016)
  list << Car.new("Toyota", "Camry", 2015)
  list << Car.new("Nissan", "Altima", 2014)

  p list.map {|p| p.model}

O mapa fornece valores iterativos por meio de uma matriz retornada pelos parâmetros do bloco.

gkstr1
fonte
mapa é exatamente o mesmo que coletar.
BKSpurgeon
0

#each

#eachexecuta uma função para cada elemento em uma matriz. Os dois trechos de código a seguir são equivalentes:

x = 10
["zero", "one", "two"].each{|element|
    x++
    puts element
}
x = 10
array = ["zero", "one", "two"]

for i in 0..2
    x++
    puts array[i]
end

#map

#mapaplica uma função a cada elemento de uma matriz, retornando a matriz resultante. Os seguintes são equivalentes:

array = ["zero", "one", "two"]
newArray = array.map{|element| element.capitalize()}
array = ["zero", "one", "two"]

newArray = []
array.each{|element|
    newArray << element.capitalize()
}

#map!

#map!é como #map, mas modifica a matriz no lugar. Os seguintes são equivalentes:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
Jivan Pal
fonte