Apa yang dilakukan metode "peta" di Ruby?

250

Saya baru dalam pemrograman. Dapatkah seseorang menjelaskan apa yang .mapakan dilakukan di:

params = (0...param_count).map
kentang besar
sumber
9
Ajukan satu pertanyaan sekaligus. mapadalah metode "fungsional" umum yang ditemukan pada objek Enumerable yang digunakan untuk mengubah nilai-nilai dalam urutan (dengan pertimbangan khusus). ..dan ...cara membuat rentang. Juga, biasakan diri dengan REPL, di mana Anda dapat mencoba sendiri hal-hal ini! :)
5
REPL untuk ruby ​​adalah irb, untuk Rails adalah rails c. REPL memungkinkan Anda untuk menguji kode secara langsung terhadap shell bahasa itu sendiri.
Gary

Jawaban:

431

The mapMetode mengambil obyek enumerable dan blok, dan berjalan blok untuk setiap elemen, keluaran setiap nilai kembali dari blok (objek asli tidak berubah kecuali jika Anda menggunakan map!):

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

Arraydan Rangemerupakan tipe enumerable. mapdengan blok mengembalikan Array. map!bermutasi array asli.

Di mana ini membantu, dan apa perbedaan antara map!dan each? Berikut ini sebuah contoh:

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

Hasil:

Danil is a programmer
Edmund is a programmer
Danil Speransky
sumber
3
terima kasih speransky untuk contohnya. lalu bagaimana .map berbeda dari .each?
bigpotato
2
Ahhh aku mengerti. Jadi .map sebenarnya mengubah array sementara .each hanya loop melalui array untuk mengakses nilai-nilai sambil membiarkan array asli tidak tersentuh?
bigpotato
24
Berbahaya bagi pembaca biasa bahwa kalimat pembuka menggambarkan mapseolah-olahmap!
kaleidic
12
untuk melihat perbedaan antara peta dan masing-masing, buka jendela IRB dan lihat hasilnya untuk y dan z dalam kode berikut: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
davej
7
@Inquisitive: 'Masing-masing' mengembalikan array yang memanggilnya (dalam contoh, [1,2,3]) ketika sebuah blok disediakan, 'map' mengembalikan sebuah array baru yang diisi dengan nilai-nilai yang dihitung oleh blok tersebut. Ini mungkin membantu: mengatur variabel ary = [1,2,3], dan periksa itu object_id. Kemudian jalankan y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1}. Sekarang periksa object_id tentang y dan z. y memiliki object_id yang sama dengan ary (karena setiap ary yang dikembalikan), tetapi z memiliki object_id yang berbeda, karena peta mengembalikan array baru.
davej
66

map, bersama dengan selectdan eachmerupakan salah satu workhorses Ruby dalam kode saya.

Ini memungkinkan Anda untuk menjalankan operasi pada setiap objek array Anda dan mengembalikan semuanya di tempat yang sama. Contohnya adalah menambah array angka dengan satu:

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

Jika Anda dapat menjalankan metode tunggal pada elemen array Anda, Anda dapat melakukannya dengan gaya tulisan singkat seperti:

  1. Untuk melakukan ini dengan contoh di atas Anda harus melakukan sesuatu seperti ini

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. Untuk lebih mudah menggunakan teknik ampersand shortcut, mari kita gunakan contoh berbeda:

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

Mengubah data di Ruby seringkali melibatkan mapoperasi kaskade . Pelajari map& select, mereka adalah beberapa metode Ruby paling berguna di perpustakaan utama. Mereka sama pentingnya dengan each.

( mapjuga merupakan alias untuk collect. Gunakan apa pun yang terbaik untuk Anda secara konseptual.)

Informasi lebih bermanfaat:

Jika objek Enumerable yang Anda jalankan eachatau mapaktif berisi sekumpulan elemen Enumerable (hashes, array), Anda dapat mendeklarasikan masing-masing elemen di dalam pipa blok Anda seperti:

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

Dalam kasus Hash (juga sebuah Enumerableobjek, Hash hanyalah sebuah array tupel dengan instruksi khusus untuk penerjemah). "Parameter pipa" pertama adalah kuncinya, yang kedua adalah nilainya.

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

Untuk menjawab pertanyaan aktual:

Dengan asumsi itu paramsadalah hash, ini akan menjadi cara terbaik untuk memetakannya: Gunakan dua parameter blok alih-alih satu untuk menangkap pasangan kunci & nilai untuk setiap tuple yang ditafsirkan dalam 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
sumber
Ini tidak berfungsi untuk saya di irb. Saya masuk NoMethodError: private method 'plusone' called for 1:Fixnumruby 2 dan 'salah jumlah args' di ruby ​​1.9 / 1.8. Pokoknya, saya menggunakan lambda: plusone = ->(x) { x + 1 }kemudian mengambil keluar specifier simbol: [1,2,3].map(&plusone).
tjmcewan
1
hmm terdengar seperti Anda menyatakan privatedi dalam kelas di mana Anda meletakkan metode Anda sebelum Anda meletakkan metode Anda
boulder_ruby
Ya, itu benar-benar terjadi. Tapi ternyata tidak. :( Pertama-tama itu dalam skrip lurus tanpa kelas, kedua di irb polos. Ini salinan / tempel kode saya: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan
Ya, saya benar-benar hanya memberikan contoh buruk dalam kode saya, saya minta maaf. Coba kode yang dimodifikasi. Ini berfungsi sekarang ...
boulder_ruby
1
@ boulder_ruby apakah ada cara untuk melakukan ini dengan metode normal - seperti pada, bukan metode kelas?
tekknolagi
6

Menggunakan ruby ​​2.4 Anda dapat melakukan hal yang sama menggunakan transform_values, fitur ini diekstraksi dari rel ke ruby.

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

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

0..param_countberarti "hingga dan termasuk param_count". 0...param_countberarti "hingga, tetapi tidak termasuk param_count".

Range#maptidak mengembalikan Enumerable, sebenarnya memetakannya ke array. Itu sama dengan Range#to_a.

Pedro Nascimento
sumber
3

Ini "memetakan" fungsi untuk setiap item dalam Enumerable- dalam hal ini, rentang. Jadi itu akan memanggil blok yang dilewati satu kali untuk setiap bilangan bulat dari 0 hingga param_count(eksklusif - Anda benar tentang titik-titik) dan mengembalikan array yang berisi setiap nilai kembali.

Inilah dokumentasi untuk Enumerable#map. Itu juga memiliki alias collect,.

Ry-
sumber
Ini aneh, tetapi Range#mapsebenarnya mengubahnya menjadi sebuah array.
Pedro Nascimento
1
@PedroNascimento: Ya ... itu yang saya katakan?
Ry-
Maaf, saya tidak menyadari bahwa peta yang dipanggil dengan sendirinya tidak mengembalikan Enumerable, seperti masing-masing. Saya pikir itu terjadi.
Pedro Nascimento
2

Peta adalah bagian dari modul enumerable. Sangat mirip dengan "kumpulkan" Misalnya:

  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}

Peta menyediakan nilai yang berulang melalui array yang dikembalikan oleh parameter blok.

gkstr1
sumber
peta persis sama dengan mengumpulkan.
BKSpurgeon
0

#each

#eachmenjalankan fungsi untuk setiap elemen dalam array. Dua kutipan kode berikut ini setara:

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

#mapmenerapkan fungsi ke setiap elemen array, mengembalikan array yang dihasilkan. Berikut ini adalah setara:

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

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

#map!

#map!seperti #map, tetapi memodifikasi array di tempatnya. Berikut ini adalah setara:

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