¿Qué hace el método de "mapa" en Ruby?

250

Soy nuevo en programación. ¿Alguien puede explicar qué .mapharía en:

params = (0...param_count).map
papa grande
fuente
99
Haz una pregunta a la vez. mapes un método "funcional" común que se encuentra en los objetos enumerables utilizados para transformar los valores en una secuencia (con consideraciones especiales). ..y ...son formas de crear rangos. Además, ¡ familiarícese con REPL, donde puede probar estas cosas usted mismo! :)
55
REPL para ruby ​​es irb, para Rails es rails c. REPL le permite probar el código directamente contra el lenguaje en sí.
Gary

Respuestas:

431

El mapmétodo toma un objeto enumerable y un bloque, y ejecuta el bloque para cada elemento, generando cada valor devuelto desde el bloque (el objeto original no cambia a menos que use map!):

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

Arrayy Rangeson tipos enumerables. mapcon un bloque devuelve una matriz. map!Muta la matriz original.

¿Dónde es útil esto y cuál es la diferencia entre map!y each? Aquí hay un ejemplo:

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

La salida:

Danil is a programmer
Edmund is a programmer
Danil Speransky
fuente
3
Gracias Speransky por el ejemplo. entonces, ¿cómo es .map diferente de .each?
bigpotato
2
Ahhh lo entiendo. Entonces, .map en realidad muta la matriz mientras .each simplemente recorre la matriz para acceder a los valores y deja la matriz original intacta.
bigpotato
24
Es peligroso para los lectores casuales que la primera frase describe mapcomo si fueramap!
kaleidic
12
para ver la diferencia entre el mapa y cada uno, abra una ventana IRB y observe los resultados para y y z en el siguiente código: y = [1,2,3]. cada {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej
77
@Inquisitivo: 'cada' devuelve la matriz que lo llama (en el ejemplo, [1,2,3]) cuando se suministra un bloque, 'mapa' devuelve una nueva matriz poblada con los valores calculados por el bloque. Esto podría ayudar: establezca la variable ary = [1,2,3], y revise su object_id. Luego ejecute y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Ahora verifique los object_id de y y z. y tiene el mismo object_id que ary (porque cada uno devuelve ary), pero z tiene un object_id diferente, porque el mapa devolvió una nueva matriz.
davej
66

map, junto con selecty eaches uno de los caballos de batalla de Ruby en mi código.

Le permite ejecutar una operación en cada uno de los objetos de su matriz y devolverlos a todos en el mismo lugar. Un ejemplo sería incrementar una matriz de números en uno:

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

Si puede ejecutar un método único en los elementos de su matriz, puede hacerlo en un estilo abreviado como este:

  1. Para hacer esto con el ejemplo anterior, tendría que hacer algo como esto

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
    
  2. Para usar más simplemente la técnica de ampersand shortcut, usemos un ejemplo diferente:

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

La transformación de datos en Ruby a menudo implica una cascada de mapoperaciones. Estudie mapy select, son algunos de los métodos Ruby más útiles en la biblioteca primaria. Son tan importantes como each.

( maptambién es un alias para collect. Usa lo que funcione mejor para ti conceptualmente).

Más información útil:

Si el objeto Enumerable en el que está ejecutando eacho mapcontiene un conjunto de elementos Enumerable (hashes, matrices), puede declarar cada uno de esos elementos dentro de sus tuberías de bloque de la siguiente manera:

[["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

En el caso de un Hash (también un Enumerableobjeto, un Hash es simplemente un conjunto de tuplas con instrucciones especiales para el intérprete). El primer "parámetro de tubería" es la clave, el segundo es el 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 la pregunta real:

Suponiendo que paramses un hash, esta sería la mejor manera de mapearlo: use dos parámetros de bloque en lugar de uno para capturar el par clave y valor para cada tupla interpretada en el 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
fuente
Esto no funciona para mí en irb. Entro NoMethodError: private method 'plusone' called for 1:Fixnumen ruby ​​2 y 'número incorrecto de args' en ruby ​​1.9 / 1.8. De todos modos, he usado un lambda: plusone = ->(x) { x + 1 }a continuación, sacar el símbolo especificador: [1,2,3].map(&plusone).
tjmcewan
1
hmm suena como si declaraste privatedentro de la clase donde pusiste tu método antes de poner tu método
boulder_ruby
Sí, lo hace totalmente. Excepto que no lo fue. :( En primer lugar, estaba en un script directo sin clases, en segundo lugar en irb simple. Aquí está mi copia / pega de su código: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan
Sí, puse un mal ejemplo en mi código, lo siento. Prueba el código modificado. Funciona ahora ...
boulder_ruby
1
@boulder_ruby ¿hay alguna manera de hacer esto con un método normal, como en, no con un método de clase?
tekknolagi 01 de
6

Usando ruby ​​2.4 puedes hacer lo mismo usando transform_valuesesta característica extraída de rieles a ruby.

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

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

0..param_countsignifica "hasta e incluido param_count". 0...param_countsignifica "hasta, pero sin incluir param_count".

Range#mapno devuelve un Enumerable, en realidad lo asigna a una matriz. Es lo mismo que Range#to_a.

Pedro Nascimento
fuente
3

"Asigna" una función a cada elemento en un Enumerable- en este caso, un rango. Por lo tanto, llamaría al bloque pasado una vez por cada entero de 0 a param_count(exclusivo: tiene razón sobre los puntos) y devolvería una matriz que contiene cada valor de retorno.

Aquí está la documentación para Enumerable#map. También tiene un alias, collect.

Ry-
fuente
Es raro, pero en Range#maprealidad lo convierte en una matriz.
Pedro Nascimento
1
@PedroNascimento: Sí ... ¿eso es lo que dije?
Ry-
Lo siento, no sabía que el mapa llamado solo no devolviera un Enumerable, como cada uno. Pensé que sí.
Pedro Nascimento
2

El mapa es una parte del módulo enumerable. Muy similar a "recoger" Por ejemplo:

  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}

Map proporciona valores que se repiten en una matriz que son devueltos por los parámetros del bloque.

gkstr1
fuente
El mapa es exactamente lo mismo que recoger.
BKSpurgeon
0

#each

#eachejecuta una función para cada elemento en una matriz. Los siguientes dos extractos de código son 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 una función a cada elemento de una matriz, devolviendo la matriz resultante. Los siguientes son equivalentes:

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

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

#map!

#map!es como #map, pero modifica la matriz en su lugar. Los siguientes son equivalentes:

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