วิธี“ แผนที่” ในทับทิมทำอะไร?


250

ฉันใหม่กับการเขียนโปรแกรม ใครสามารถอธิบายสิ่งที่.mapจะทำใน:

params = (0...param_count).map

9
ถามคำถามหนึ่งครั้ง mapเป็นวิธีการทั่วไป "ใช้งานได้" ที่พบในวัตถุนับจำนวนที่ใช้สำหรับเปลี่ยนค่าในลำดับ (โดยมีข้อควรพิจารณาพิเศษ) ..และ...เป็นวิธีในการสร้างช่วง นอกจากนี้ยังได้คุ้นเคยกับ REPL ที่คุณสามารถลองสิ่งนี้ด้วยตัวคุณเอง! :)

5
REPL สำหรับ ruby ​​คือ irb สำหรับ Rails คือ Rails c. REPL ให้คุณทดสอบโค้ดโดยตรงกับเชลล์ภาษาเอง
Gary

คำตอบ:


431

mapวิธีการใช้เวลานับวัตถุและกระชากและวิ่งบล็อกสำหรับแต่ละองค์ประกอบ, การแสดงผลแต่ละค่ากลับมาจากบล็อก (วัตถุเดิมไม่เปลี่ยนแปลงเว้นแต่คุณจะใช้map!):

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

ArrayและRangeเป็นประเภทที่นับได้ mapด้วยบล็อกส่งกลับอาร์เรย์ map!กลายพันธุ์อาร์เรย์เดิม

สิ่งนี้มีประโยชน์ที่ไหนและอะไรคือความแตกต่างระหว่างmap!และeach? นี่คือตัวอย่าง:

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

ผลลัพธ์:

Danil is a programmer
Edmund is a programmer

3
ขอบคุณ speransky สำหรับตัวอย่าง แล้ว. map แตกต่างจาก. each อย่างไร
bigpotato

2
อ่าฉันเข้าใจแล้ว ดังนั้น. map จะทำการเปลี่ยนแปลงอาร์เรย์ในขณะที่. แต่ละครั้งจะวนซ้ำผ่านอาร์เรย์เพื่อเข้าถึงค่าในขณะที่ปล่อยให้อาร์เรย์เดิมไม่มีการแตะต้อง?
bigpotato

24
มันเป็นสิ่งที่อันตรายสำหรับผู้อ่านทั่วไปที่ประโยคเปิดอธิบายmapว่าmap!
kaleidic

12
หากต้องการดูความแตกต่างระหว่างแผนที่และแต่ละแผนที่ให้เปิดหน้าต่าง IRB และดูผลลัพธ์สำหรับ y และ z ในรหัสต่อไปนี้: y = [1,2,3] .each {| x | x + 1}; z = [1,2,3] .map {| x | x + 1}
เดฟ

7
@Inquisitive: 'แต่ละ' ส่งคืนอาร์เรย์ที่เรียกมันว่า (ในตัวอย่าง [1,2,3]) เมื่อมีการจัดบล็อกให้ 'map' ส่งคืนอาร์เรย์ใหม่ที่มีค่าที่คำนวณโดยบล็อก สิ่งนี้อาจช่วยได้: ตั้งค่าตัวแปร ary = [1,2,3] และตรวจสอบว่าเป็น object_id จากนั้นเรียกใช้ y = ary.each {| x | x + 1}; z = ary.map {| x | x + 1} ตอนนี้ตรวจสอบ object_id ของ y และ z y มี object_id เช่นเดียวกับ ary (เพราะแต่ละคืน ary) แต่ z มี object_id ที่แตกต่างกันเนื่องจากแผนที่ส่งคืนอาร์เรย์ใหม่
davej

66

mapพร้อมกับselectและeachเป็นหนึ่งในผลงานของ Ruby ในรหัสของฉัน

ช่วยให้คุณสามารถดำเนินการกับวัตถุของอาร์เรย์และคืนค่าทั้งหมดในที่เดียวกัน ตัวอย่างคือการเพิ่มอาร์เรย์ของตัวเลขโดยหนึ่ง:

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

หากคุณสามารถเรียกใช้วิธีการเดียวกับองค์ประกอบของอาร์เรย์คุณสามารถทำได้ในรูปแบบชวเลข

  1. เมื่อต้องการทำสิ่งนี้กับตัวอย่างด้านบนคุณต้องทำอะไรแบบนี้

    class Numeric
      def plusone
        self + 1
      end
    end
    [1,2,3].map(&:plusone)
    #=> [2,3,4]
  2. หากต้องการใช้เทคนิคเครื่องหมายทางลัดและแอมเปอร์แซนด์มากขึ้นให้ใช้ตัวอย่างอื่น:

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

การแปลงข้อมูลใน Ruby มักเกี่ยวข้องกับการmapดำเนินการทั้งหมด การศึกษาmap& selectพวกเขาเป็นวิธีทับทิมที่มีประโยชน์ที่สุดในห้องสมุดหลัก มันสำคัญเหมือนeachกัน

( mapเป็นชื่อแทนสำหรับcollectใช้สิ่งที่ดีที่สุดสำหรับคุณในเชิงแนวคิด)

ข้อมูลที่เป็นประโยชน์เพิ่มเติม:

หากEnumerableวัตถุที่คุณกำลังทำงานeachหรือmapบนมีชุดขององค์ประกอบ Enumerable (แฮชอาร์เรย์) คุณสามารถประกาศแต่ละองค์ประกอบเหล่านั้นภายในท่อบล็อกของคุณต้องการเพื่อ:

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

ในกรณีของแฮช (เช่นเดียวกับEnumerableวัตถุแฮชคือชุดของทูเปิลพร้อมคำแนะนำพิเศษสำหรับล่าม) "พารามิเตอร์พารามิเตอร์" ตัวแรกคือคีย์ตัวที่สองคือค่า

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

ในการตอบคำถามจริง:

สมมติว่าparamsเป็นแฮชนี่จะเป็นวิธีที่ดีที่สุดในการแมปผ่านมัน: ใช้พารามิเตอร์บล็อกสองรายการแทนที่จะเป็นพารามิเตอร์หนึ่งเพื่อจับคู่คีย์ & ค่าสำหรับแต่ละ tuple ที่ถูกตีความในแฮช

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

มันใช้งานไม่ได้สำหรับฉันใน irb ฉันได้NoMethodError: private method 'plusone' called for 1:Fixnumทับทิม 2 และ 'args number ผิด' ใน ruby ​​1.9 / 1.8 อย่างไรก็ตามผมใช้แลมบ์ดา: จากนั้นนำออกมาระบุสัญลักษณ์:plusone = ->(x) { x + 1 } [1,2,3].map(&plusone)
tjmcewan

1
อืมดูเหมือนว่าคุณประกาศprivateไว้ในชั้นเรียนที่คุณวางวิธีการของคุณก่อนที่คุณจะวางวิธีการของคุณ
boulder_ruby

ใช่มันทำทั้งหมด ยกเว้นว่ามันจะไม่ :( อย่างแรกเลยมันอยู่ในสคริปตรง w / o คลาสที่สองใน irb ธรรมดานี่คือสำเนาของฉัน / วางรหัสของคุณ: gist.github.com/tjmcewan/a7e4feb2976a93a5eef9
tjmcewan

ใช่ฉันแค่ใส่ตัวอย่างที่ไม่ดีในรหัสของฉันฉันขอโทษ ลองแก้ไขรหัส มันใช้งานได้แล้ว ...
boulder_ruby

1
@boulder_ruby มีวิธีการทำเช่นนี้ด้วยวิธีการปกติ - ในขณะที่ไม่ใช่วิธีการเรียน?
tekknolagi

6

การใช้ทับทิม 2.4 คุณสามารถทำสิ่งเดียวกันได้โดยใช้transform_valuesคุณสมบัตินี้สกัดจากรางไปยังทับทิม

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

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

4

0..param_countหมายถึง "ถึงและรวมถึง param_count" 0...param_countหมายถึง "ไม่เกิน แต่ไม่รวม param_count"

Range#mapไม่ส่งคืนEnumerableมันจริงแมปไปยังอาร์เรย์ Range#to_aมันเป็นเช่นเดียวกับ


3

มัน "แมป" ฟังก์ชั่นสำหรับแต่ละรายการในEnumerable- ในกรณีนี้คือช่วง ดังนั้นมันจะเรียกบล็อกที่ส่งผ่านหนึ่งครั้งสำหรับทุกจำนวนเต็มตั้งแต่ 0 ถึงparam_count(พิเศษ - คุณอยู่ในจุดที่ถูกต้อง) และส่งกลับอาร์เรย์ที่มีค่าส่งคืนแต่ละค่า

Enumerable#mapนี่คือเอกสารสำหรับ collectนอกจากนี้ยังมีนามแฝง


มันแปลก แต่Range#mapจริงๆแล้วมันแปลงเป็นอาเรย์
Pedro Nascimento

1
@PedroNascimento: ใช่ ... นั่นคือสิ่งที่ฉันพูด?
Ry-

ขออภัยฉันไม่ทราบว่าแผนที่ที่เรียกตัวเองนั้นไม่ได้กลับมาEnumerableเหมือนกัน ฉันคิดว่ามันทำ
Pedro Nascimento

2

แผนที่เป็นส่วนหนึ่งของโมดูลที่นับได้ คล้ายกับ "รวบรวม" ตัวอย่างเช่น:

  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}

แผนที่แสดงค่าที่วนซ้ำผ่านอาร์เรย์ที่ส่งคืนโดยพารามิเตอร์บล็อก


แผนที่เหมือนกันกับการรวบรวม
BKSpurgeon

0

#each

#eachเรียกใช้ฟังก์ชันสำหรับแต่ละองค์ประกอบในอาร์เรย์ ข้อความที่ตัดตอนมาของรหัสสองต่อไปนี้เทียบเท่า:

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

#mapใช้ฟังก์ชั่นกับองค์ประกอบของอาร์เรย์แต่ละคืนกลับอาร์เรย์ผล สิ่งต่อไปนี้เทียบเท่า:

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

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

#map!

#map!เป็นเหมือน#mapแต่ปรับเปลี่ยนอาร์เรย์ในสถานที่ สิ่งต่อไปนี้เทียบเท่า:

array = ["zero", "one", "two"]
array.map!{|element| element.capitalize()}
array = ["zero", "one", "two"]
array = array.map{|element| element.capitalize()}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.