วิธีแมปกับดัชนีในรูบีอย่างไร


คำตอบ:


835

หากคุณใช้ ruby ​​1.8.7 หรือ 1.9 คุณสามารถใช้ความจริงที่ว่า iterator method เช่นeach_with_indexเมื่อถูกเรียกโดยไม่มีบล็อกให้ส่งคืนEnumeratorออบเจกต์ซึ่งคุณสามารถเรียกEnumerableเมธอดเช่นmapนี้ได้ ดังนั้นคุณสามารถทำได้:

arr.each_with_index.map { |x,i| [x, i+2] }

ใน 1.8.6 คุณสามารถทำได้:

require 'enumerator'
arr.enum_for(:each_with_index).map { |x,i| [x, i+2] }

ขอบคุณ! คุณช่วยชี้ให้ฉันเอกสารประกอบได้.each_with_index.mapหรือไม่
Misha Moroshko

1
@Misha: mapเป็นวิธีการEnumerableเช่นเคย each_with_indexเมื่อเรียกโดยไม่มีบล็อกส่งกลับEnumeratorวัตถุ (ใน 1.8.7+) ซึ่งผสมในEnumerableเพื่อให้คุณสามารถโทรmap, select, rejectฯลฯ กับมันเช่นเดียวกับบนอาร์เรย์กัญชาช่วง ฯลฯ
sepp2k

9
IMO นี้ง่ายขึ้นและอ่านได้ดีขึ้นใน 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz

4
@Phrogz: map.with_indexไม่ทำงานใน 1.8.7 ( mapคืนค่าอาร์เรย์เมื่อถูกเรียกโดยไม่มีบล็อกใน 1.8)
sepp2k

2
สิ่งสำคัญที่ควรทราบคือสิ่งนี้ใช้ไม่ได้กับ. map! หากคุณต้องการส่งผลกระทบโดยตรงกับอาร์เรย์ที่คุณวนลูป
Ash Blue

258

Ruby มีEnumerator # with_index (offset = 0)ดังนั้นก่อนอื่นให้แปลง Array เป็น Enumerator โดยใช้Object # to_enumหรือArray # map :

[:a, :b, :c].map.with_index(2).to_a
#=> [[:a, 2], [:b, 3], [:c, 4]]

11
ฉันเชื่อว่านี่เป็นคำตอบที่ดีกว่าเพราะมันจะใช้ได้กับแผนที่! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Connor McKay

130

ในทับทิม 1.9.3 มีวิธีการแบบลูกโซ่ที่เรียกว่าwith_indexซึ่งสามารถผูกมัดกับแผนที่ได้

ตัวอย่างเช่น:

array.map.with_index { |item, index| ... }

17

เหนือความงงงวยด้านบน:

arr = ('a'..'g').to_a
indexes = arr.each_index.map(&2.method(:+))
arr.zip(indexes)

12
แอนดรูว์ต้องมีความปลอดภัยในการทำงานที่ยอดเยี่ยม! :)
David J.

9

นี่คือสองตัวเลือกเพิ่มเติมสำหรับ 1.8.6 (หรือ 1.9) โดยไม่ใช้ตัวแจงนับ:

# Fun with functional
arr = ('a'..'g').to_a
arr.zip( (2..(arr.length+2)).to_a )
#=> [["a", 2], ["b", 3], ["c", 4], ["d", 5], ["e", 6], ["f", 7], ["g", 8]]

# The simplest
n = 1
arr.map{ |c| [c, n+=1 ] }
#=> [["a", 2], ["b", 3], ["c", 4], ["d", 5], ["e", 6], ["f", 7], ["g", 8]]

8

ฉันชอบไวยากรณ์ของรูปแบบนี้:

a = [1, 2, 3, 4]
a.each_with_index.map { |el, index| el + index }
# => [1, 3, 5, 7]

การเรียกใช้each_with_indexจะทำให้คุณมีตัวแจงนับคุณสามารถแมปดัชนีของคุณได้อย่างง่ายดาย


4
รูปแบบนี้แตกต่างกันอย่างไรคำตอบให้เกือบ 5 ปีก่อนของคุณ ?
Andrey Deineko

4

วิธีที่สนุก แต่ไร้ประโยชน์ในการทำเช่นนี้:

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}

ทำไมถึงเกลียด? มันเป็นวิธีการทำงานของการทำสิ่งนี้และฉันก็บอกว่ามันเป็นวิธีที่โง่เพื่อให้ได้ผลลัพธ์
Automatico

การเรียกไปยัง #index หมายความว่าตอนนี้เป็นลูป O (N ^ 2) ด้วยเหตุใดจึง +2 :)
rogerdpack

2
A fun, but useless wayขณะที่ผมเขียน +2คือการสร้างผลลัพธ์ที่ OP ขอให้
Automatico


1
module Enumerable
  def map_with_index(&block)
    i = 0
    self.map { |val|
      val = block.call(val, i)
      i += 1
      val
    }
  end
end

["foo", "bar"].map_with_index {|item, index| [item, index] } => [["foo", 0], ["bar", 1]]

3
พระเจ้าช่วย! คุณอ่านคำตอบอื่น ๆ ด้วยซ้ำหรือไม่? map.with_indexมีอยู่แล้วในทับทิม เหตุใดจึงแนะนำให้เปิดคลาสที่นับได้อีกครั้งและเพิ่มสิ่งที่มีอยู่แล้ว
nathanvda

นี่อาจเป็นวิธีที่ง่ายกว่าสำหรับรุ่น 1.8.6 และ 1.8.7 (ใช่เราบางคนยังคงใช้มัน) แทนที่จะต้องใช้สิ่งที่แปลกใหม่เช่นeach_with_index.mapฯลฯ และแม้แต่พวกเราในเวอร์ชั่นใหม่อาจต้องการใช้ map.with_index FWIW :)
rogerdpack

1

ฉันมักจะทำสิ่งนี้:

arr = ["a", "b", "c"]

(0...arr.length).map do |int|
  [arr[int], int + 2]
end

#=> [["a", 2], ["b", 3], ["c", 4]]

แทนที่จะวนซ้ำโดยตรงกับองค์ประกอบของอาร์เรย์คุณจะวนซ้ำในช่วงจำนวนเต็มและใช้มันเป็นดัชนีเพื่อดึงองค์ประกอบของอาร์เรย์


1
หากคุณอ่านคำตอบอื่น ๆ ฉันหวังว่าคุณจะรู้ว่ามีวิธีการที่ดีกว่า ดังนั้นไม่แน่ใจว่าทำไมคุณต้องเพิ่มสิ่งนี้
nathanvda

ถ้าคำตอบของแอนดรูว์กริมม์สมควรได้รับสิบคะแนนจากนั้นก็สมควรได้รับคะแนนนี้อย่างน้อยหนึ่งครั้ง!
Camille Goudeseune
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.