Was macht die "Map" -Methode in Ruby?

250

Ich bin neu in der Programmierung. Kann jemand erklären, was .mapin tun würde:

params = (0...param_count).map
Bigpotato
quelle
9
Stellen Sie jeweils eine Frage. mapist eine gebräuchliche "funktionale" Methode für Enumerable-Objekte, die zum Transformieren der Werte in einer Sequenz verwendet werden (mit besonderen Überlegungen). ..und ...sind Möglichkeiten, Bereiche zu erstellen. Auch vertraut mit dem REPL, wo Sie das Zeug selbst ausprobieren können! :)
5
REPL für Rubin ist irb, für Rails ist es Rails c. Mit REPL können Sie Code direkt anhand der Sprachshell selbst testen.
Gary

Antworten:

431

Die mapMethode verwendet ein aufzählbares Objekt und einen Block und führt den Block für jedes Element aus, wobei jeder zurückgegebene Wert aus dem Block ausgegeben wird (das ursprüngliche Objekt bleibt unverändert, es sei denn, Sie verwenden map!):

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

Arrayund Rangesind unzählige Typen. mapmit einem Block gibt ein Array zurück. map!mutiert das ursprüngliche Array.

Wo ist das hilfreich und was ist der Unterschied zwischen map!und each? Hier ist ein Beispiel:

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

Die Ausgabe:

Danil is a programmer
Edmund is a programmer
Danil Speransky
quelle
3
danke speransky für das beispiel. Wie unterscheidet sich .map dann von .each?
Bigpotato
2
Ahhh ich verstehe. Also mutiert .map das Array tatsächlich, während .each nur das Array durchläuft, um auf die Werte zuzugreifen, während das ursprüngliche Array unberührt bleibt?
Bigpotato
24
Es ist gefährlich für Gelegenheitsleser, dass der Eröffnungssatz beschreibt, mapals wäre esmap!
kaleidisch
12
Um den Unterschied zwischen Karte und Karte zu erkennen, öffnen Sie ein IRB-Fenster und sehen Sie sich die Ergebnisse für y und z im folgenden Code an: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
Davej
7
@Inquisitive: 'each' gibt das Array zurück, das es aufruft (im Beispiel [1,2,3]), wenn ein Block angegeben wird. 'Map' gibt ein neues Array zurück, das mit den vom Block berechneten Werten gefüllt ist. Dies könnte helfen: Setzen Sie die Variable ary = [1,2,3] und überprüfen Sie die Objekt-ID. Führen Sie dann y = ary.each {| x | aus x + 1}; z = ary.map {| x | x + 1}. Überprüfen Sie nun die Objekt-IDs von y und z. y hat dieselbe object_id wie ary (weil jeder ary zurückgegeben hat), aber z hat eine andere object_id, weil map ein neues Array zurückgegeben hat.
Davej
66

map, zusammen mit selectund eachist eines von Rubys Arbeitspferden in meinem Code.

Sie können damit eine Operation für jedes Objekt Ihres Arrays ausführen und alle an derselben Stelle zurückgeben. Ein Beispiel wäre, ein Array von Zahlen um eins zu erhöhen:

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

Wenn Sie eine einzelne Methode für die Elemente Ihres Arrays ausführen können, können Sie dies in einer Kurzform wie folgt tun:

  1. Um dies mit dem obigen Beispiel zu tun, müssten Sie so etwas tun

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
    
  2. Um die kaufmännische Und-Verknüpfungstechnik einfacher zu verwenden, verwenden wir ein anderes Beispiel:

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

Das Transformieren von Daten in Ruby ist häufig mit einer Kaskade von mapVorgängen verbunden. Study map& select, sie sind einige der nützlichsten Ruby-Methoden in der Primärbibliothek. Sie sind genauso wichtig wie each.

( mapist auch ein Alias ​​für collect. Verwenden Sie konzeptionell das, was für Sie am besten funktioniert.)

Weitere hilfreiche Informationen:

Wenn das Enumerable- Objekt, das Sie ausführeneach oder mapauf dem Sie arbeiten, eine Reihe von Enumerable-Elementen (Hashes, Arrays) enthält, können Sie jedes dieser Elemente in Ihren Blockpipes wie folgt deklarieren:

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

Im Fall eines Hash (auch ein EnumerableObjekt, ist ein Hash einfach ein Array von Tupeln mit speziellen Anweisungen für den Interpreter). Der erste "Pipe-Parameter" ist der Schlüssel, der zweite ist der Wert.

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

Um die eigentliche Frage zu beantworten:

Angenommen, es paramshandelt sich um einen Hash, ist dies der beste Weg, um ihn abzubilden: Verwenden Sie zwei Blockparameter anstelle von einem, um das Schlüssel-Wert-Paar für jedes interpretierte Tupel im Hash zu erfassen.

params = {"one" => 1, "two" => 2, "three" => 3}
params.each do |k,v|
  puts "#{k}=#{v}"
end
# one=1
# two=2
# three=3
boulder_ruby
quelle
Das funktioniert bei mir in irb nicht. Ich bekomme NoMethodError: private method 'plusone' called for 1:Fixnumin Rubin 2 und 'falsche Anzahl von Argumenten' in Rubin 1.9 / 1.8. Wie auch immer, ich habe ein Lambda verwendet: plusone = ->(x) { x + 1 }Nehmen Sie dann den Symbolbezeichner heraus : [1,2,3].map(&plusone).
tjmcewan
1
hmm hört sich so an, als hätten Sie privateinnerhalb der Klasse deklariert, in der Sie Ihre Methode abgelegt haben, bevor Sie Ihre Methode
abgelegt haben
Ja, das tut es total. Nur dass es nicht so war. :( Erstens war es in einem geraden Skript ohne Klassen, zweitens in einfachem irb. Hier ist mein Kopieren / Einfügen Ihres Codes: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan
Ja, ich habe nur ein schlechtes Beispiel in meinen Code eingefügt. Es tut mir leid. Probieren Sie den geänderten Code aus. Es funktioniert jetzt ...
boulder_ruby
1
@boulder_ruby gibt es eine Möglichkeit, dies mit einer normalen Methode zu tun - wie in, nicht mit einer Klassenmethode?
Tekknolagi
6

Mit Ruby 2.4 können Sie dasselbe tun, indem Sie transform_valuesdiese Funktion von Schienen zu Ruby extrahieren.

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

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

0..param_countbedeutet "bis einschließlich param_count". 0...param_countbedeutet "bis zu, aber ohne param_count".

Range#mapgibt kein zurück Enumerable, sondern ordnet es tatsächlich einem Array zu. Es ist das gleiche wie Range#to_a.

Pedro Nascimento
quelle
3

Es "ordnet" jedem Element in einem Enumerable- in diesem Fall einem Bereich - eine Funktion zu . Es würde also den Block, der einmal für jede Ganzzahl von 0 bis übergeben wurde param_count(exklusiv - Sie haben Recht mit den Punkten), aufrufen und ein Array zurückgeben, das jeden Rückgabewert enthält.

Hier ist die Dokumentation für Enumerable#map. Es hat auch einen Alias collect.

Ry-
quelle
Es ist komisch, Range#mapkonvertiert es aber tatsächlich in ein Array.
Pedro Nascimento
1
@ PedroNascimento: Ja ... das habe ich gesagt?
Ry-
Entschuldigung, mir war nicht bewusst, dass die von sich selbst aufgerufene Karte nicht Enumerablewie jede eine zurückgab. Ich dachte es wäre so.
Pedro Nascimento
2

Map ist Teil des Aufzählungsmoduls. Sehr ähnlich zu "sammeln" Zum Beispiel:

  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 bietet Werte, die durch ein Array iterieren und von den Blockparametern zurückgegeben werden.

gkstr1
quelle
Karte ist genau das gleiche wie sammeln.
BKSpurgeon
0

#each

#eachführt eine Funktion für jedes Element in einem Array aus. Die folgenden zwei Code-Auszüge sind äquivalent:

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

#mapWendet eine Funktion auf jedes Element eines Arrays an und gibt das resultierende Array zurück. Folgendes ist äquivalent:

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

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

#map!

#map!ist wie #map, ändert aber das Array an Ort und Stelle. Folgendes ist äquivalent:

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