Làm thế nào để lập bản đồ với chỉ mục trong Ruby?


438

Cách dễ nhất để chuyển đổi là gì

[x1, x2, x3, ... , xN]

đến

[[x1, 2], [x2, 3], [x3, 4], ... , [xN, N+1]]

Câu trả lời:


835

Nếu bạn đang sử dụng ruby ​​1.8.7 hoặc 1.9, bạn có thể sử dụng thực tế là các phương thức lặp như each_with_indexkhi được gọi mà không có khối, trả về một Enumeratorđối tượng mà bạn có thể gọi Enumerablecác phương thức như maptrên. Vì vậy, bạn có thể làm:

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

Trong 1.8.6 bạn có thể làm:

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

Cảm ơn! Bạn có thể cho tôi một con trỏ đến tài liệu cho .each_with_index.map?
Misha Moroshko

1
@Misha: maplà một phương pháp Enumerablenhư mọi khi. each_with_index, Khi gọi mà không có khối, trả về một Enumeratorđối tượng (trong 1.8.7+), trong đó hỗn hợp trong Enumerable, vì vậy bạn có thể gọi map, select, rejectvv trên nó giống như trên một mảng, băm, dao động, vv
sepp2k

9
IMO điều này đơn giản và đọc tốt hơn trong 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz

4
@Phrogz: map.with_indexkhông hoạt động trong 1.8.7 ( maptrả về một mảng khi được gọi mà không có khối trong 1.8).
sepp2k

2
Điều quan trọng cần lưu ý là điều này không hoạt động với .map! nếu bạn muốn ảnh hưởng trực tiếp đến mảng bạn đang lặp.
Ash Blue

258

Ruby có Enumerator # with_index (offset = 0) , vì vậy trước tiên hãy chuyển đổi mảng thành một liệt kê bằng Object # to_enum hoặc Array # map :

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

11
Tôi tin rằng đây là câu trả lời tốt hơn, bởi vì nó sẽ hoạt động với bản đồ! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Connor McKay

130

Trong ruby ​​1.9.3, có một phương pháp with_indexcó thể gọi là chuỗi có thể được xâu chuỗi để ánh xạ.

Ví dụ:

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

17

Trên obfuscation hàng đầu:

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

12
Andrew phải có bảo mật công việc tuyệt vời! :)
David J.

9

Dưới đây là hai tùy chọn khác cho 1.8.6 (hoặc 1.9) mà không cần sử dụng liệt kê:

# 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]]


4

Một cách thú vị, nhưng vô dụng để làm điều này:

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

Tại sao lại ghét? Đó là một cách hoạt động để làm điều này VÀ tôi thậm chí nói rằng đó là một cách ngớ ngẩn để đạt được kết quả.
Automatico

lệnh gọi #index có nghĩa đây là vòng lặp O (N ^ 2) tại sao lại là +2? :)
rogerdpack

2
Khi tôi viết A fun, but useless way. +2là để tạo đầu ra mà OP yêu cầu
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
CHÚA ƠI! Bạn thậm chí đã đọc các câu trả lời khác? map.with_indexđã tồn tại trong ruby. Tại sao đề nghị mở lại lớp vô số và thêm một cái gì đó đã tồn tại?
nathanvda

Đây có thể là một cách dễ dàng hơn để sử dụng 1.8.6 và 1.8.7 (vâng, một số người trong chúng ta vẫn sử dụng nó) thay vì phải sử dụng những thứ lạ hơn như each_with_index.mapv.v. và ngay cả những người trong chúng ta trên các phiên bản mới hơn có thể thích sử dụng nó hơn map.with_index FWIW :)
rogerdpack 13/03/2017

1

Tôi thường làm điều này:

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

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

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

Thay vì lặp trực tiếp qua các phần tử của mảng, bạn đang lặp qua một loạt các số nguyên và sử dụng chúng làm chỉ số để truy xuất các phần tử của mảng.


1
Nếu bạn đọc các câu trả lời khác, tôi hy vọng bạn nhận ra rằng có những cách tiếp cận tốt hơn. Vì vậy, không chắc chắn tại sao bạn cần phải thêm điều này.
nathanvda

Nếu câu trả lời của Andrew Grimm xứng đáng mười phiếu, thì câu này xứng đáng ít nhất một phiếu!
Camille Goudeseune
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.