Cosa fa il metodo "map" in Ruby?


250

Sono nuovo nella programmazione. Qualcuno può spiegare cosa .mapfarebbe in:

params = (0...param_count).map

9
Fai una domanda alla volta. mapè un metodo "funzionale" comune trovato su oggetti Enumerable utilizzati per trasformare i valori in una sequenza (con considerazioni speciali). ..e ...sono modi di creare intervalli. Inoltre, familiarizza con la REPL, dove puoi provare queste cose da solo! :)

5
REPL per ruby ​​è irb, per Rails è rails c. REPL consente di testare il codice direttamente sulla shell della lingua stessa.
Gary,

Risposte:


431

Il mapmetodo accetta un oggetto enumerabile e un blocco ed esegue il blocco per ciascun elemento, emettendo ogni valore restituito dal blocco (l'oggetto originale rimane invariato a meno che non si usi map!):

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

Arraye Rangesono tipi enumerabili. mapcon un blocco restituisce un array. map!muta l'array originale.

Dove è utile e qual è la differenza tra map!e each? Ecco un esempio:

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

Il risultato:

Danil is a programmer
Edmund is a programmer

3
grazie speransky per l'esempio. allora come è .map diverso da .each?
bigpotato,

2
Ahhh ho capito. Quindi .map in realtà muta l'array mentre .each fa semplicemente un giro nell'array per accedere ai valori lasciando intatta l'array originale?
bigpotato,

24
È pericoloso per i lettori occasionali che la frase di apertura lo descriva mapcome se fossemap!
caleidico il

12
per vedere la differenza tra mappa e ciascuna, apri una finestra IRB e guarda i risultati per y e z nel seguente codice: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej,

7
@Inquisitive: 'each' restituisce l'array che lo chiama (nell'esempio, [1,2,3]) quando viene fornito un blocco, 'map' restituisce un nuovo array popolato con i valori calcolati dal blocco. Questo potrebbe essere d'aiuto: imposta la variabile ary = [1,2,3], e controlla è object_id. Quindi esegui y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Ora controlla gli object_id di ye z. y ha lo stesso object_id di ary (perché ognuno ha restituito ary), ma z ha un object_id diverso, perché map ha restituito un nuovo array.
Davej,

66

map, insieme a selected eachè uno dei cavalli di lavoro di Ruby nel mio codice.

Consente di eseguire un'operazione su ciascuno degli oggetti dell'array e di restituirli tutti nello stesso posto. Un esempio potrebbe essere l'incremento di una matrice di numeri di uno:

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

Se è possibile eseguire un singolo metodo sugli elementi dell'array, è possibile farlo in uno stile abbreviato in questo modo:

  1. Per fare questo con l'esempio sopra, dovresti fare qualcosa del genere

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Per usare più semplicemente la tecnica della scorciatoia e commerciale, usiamo un esempio diverso:

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

La trasformazione dei dati in Ruby comporta spesso una serie di mapoperazioni. Studia mape select, sono alcuni dei metodi Ruby più utili nella libreria principale. Sono importanti quanto each.

( mapè anche un alias per collect. Usa ciò che funziona meglio per te concettualmente.)

Altre informazioni utili:

Se l' oggetto Enumerable che stai eseguendo eacho mapsu che contiene contiene un set di elementi Enumerable (hash, array), puoi dichiarare ciascuno di quegli elementi all'interno dei tubi del blocco in questo modo:

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

Nel caso di un hash (anche un Enumerableoggetto, un hash è semplicemente una matrice di tuple con istruzioni speciali per l'interprete). Il primo "parametro pipe" è la chiave, il secondo è il valore.

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

Per rispondere alla domanda effettiva:

Supponendo che paramssia un hash, questo sarebbe il modo migliore per mapparlo: usa due parametri di blocco invece di uno per catturare la coppia chiave-valore per ogni tupla interpretata nell'hash.

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3

Questo non funziona per me in Irlanda. Ottengo NoMethodError: private method 'plusone' called for 1:Fixnumruby 2 e "errato numero di args" in ruby ​​1.9 / 1.8. In ogni caso, ho usato un lambda: plusone = ->(x) { x + 1 }estrarre la specificatore simbolo: [1,2,3].map(&plusone).
tjmcewan,

1
hmm sembra che tu abbia dichiarato privateall'interno della classe in cui hai messo il tuo metodo prima di mettere il tuo metodo
boulder_ruby

Sì, lo fa totalmente. Solo che non lo era. :( Prima di tutto era in uno script diretto senza classi, in secondo luogo in semplice irb. Ecco la mia copia / incolla del tuo codice: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan

Sì, ho appena dato un cattivo esempio nel mio codice mi dispiace. Prova il codice modificato. Funziona ora ...
boulder_ruby,

1
@boulder_ruby c'è un modo per farlo con un metodo normale - come in, non un metodo di classe?
tekknolagi,

6

Usando ruby ​​2.4 puoi fare la stessa cosa usando transform_values, questa funzione estratta dalle rotaie al rubino.

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

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

4

0..param_countsignifica "fino a param_count compreso". 0...param_countsignifica "fino a, ma non incluso param_count".

Range#mapnon restituisce un Enumerable, in realtà lo mappa su un array. È lo stesso di Range#to_a.


3

"Mappa" una funzione per ciascun elemento in un Enumerable- in questo caso, un intervallo. Quindi chiamerebbe il blocco passato una volta per ogni numero intero da 0 a param_count(esclusivo - hai ragione circa i punti) e restituirebbe un array contenente ogni valore di ritorno.

Ecco la documentazione per Enumerable#map. Essa ha anche un alias, collect.


È strano, ma in Range#maprealtà lo converte in un array.
Pedro Nascimento,

1
@PedroNascimento: Sì ... è quello che ho detto?
Ry-

Spiacenti, non sapevo che la mappa chiamata da sola non restituiva una Enumerable, come ciascuna. Ho pensato di si.
Pedro Nascimento,

2

La mappa fa parte del modulo enumerabile. Molto simile a "raccogliere" per esempio:

  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}

Mappa fornisce valori che ripetono in un array restituiti dai parametri del blocco.


la mappa è esattamente la stessa di raccogliere.
BKSpurgeon,

0

#each

#eachesegue una funzione per ciascun elemento in un array. I seguenti due estratti di codice sono equivalenti:

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

#mapapplica una funzione a ciascun elemento di un array, restituendo l'array risultante. Sono equivalenti:

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

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

#map!

#map!è come #map, ma modifica l'array in atto. Sono equivalenti:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.