Bagaimana cara memetakan dengan indeks di Ruby?


438

Apa cara termudah untuk mengkonversi

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

untuk

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

Jawaban:


835

Jika Anda menggunakan ruby ​​1.8.7 atau 1.9, Anda dapat menggunakan fakta bahwa metode iterator seperti each_with_index, ketika dipanggil tanpa blok, mengembalikan Enumeratorobjek, yang dapat Anda panggil Enumerablemetode seperti mappada. Jadi kamu bisa melakukan:

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

Di 1.8.6 Anda dapat melakukan:

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

Terima kasih! Bisakah Anda memberi saya petunjuk untuk dokumentasi .each_with_index.map?
Misha Moroshko

1
@Misha: mapadalah metode Enumerableseperti biasa. each_with_index, Saat dipanggil tanpa blok, mengembalikan sebuah Enumeratorobjek (di 1.8.7+), yang campuran dalam Enumerable, sehingga Anda dapat memanggil map, select, rejectdll di atasnya seperti pada array, hash, berkisar dll
sepp2k

9
IMO ini lebih sederhana dan lebih mudah dibaca di 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
Phrogz

4
@ Phrogz: map.with_indextidak berfungsi di 1.8.7 ( mapmengembalikan array ketika dipanggil tanpa blok di 1.8).
sepp2k

2
Penting untuk dicatat ini tidak berfungsi dengan .map! jika Anda ingin secara langsung memengaruhi array yang Anda gunakan.
Ash Blue


130

Di ruby ​​1.9.3 ada metode chainable yang disebut with_indexyang bisa dirantai untuk memetakan.

Sebagai contoh:

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

17

Kebingungan atas:

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

12
Andrew harus memiliki keamanan pekerjaan yang hebat! :)
David J.

9

Berikut adalah dua opsi lagi untuk 1.8.6 (atau 1.9) tanpa menggunakan enumerator:

# 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

Cara yang menyenangkan, tetapi tidak berguna untuk melakukan ini:

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

Kenapa benci? Ini adalah cara yang berfungsi untuk melakukan ini DAN saya bahkan mengatakan itu adalah cara konyol untuk mencapai hasil.
Automatico

panggilan ke #index berarti sekarang ini merupakan O (N ^ 2) loop juga mengapa +2? :)
rogerdpack

2
Saat saya menulis A fun, but useless way. +2adalah untuk membuat output yang diminta 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
OH TUHAN! Apakah Anda bahkan membaca jawaban yang lain? map.with_indexsudah ada di ruby. Mengapa menyarankan untuk membuka kembali kelas enumerable dan menambahkan sesuatu yang sudah ada?
nathanvda

Ini mungkin cara yang lebih mudah untuk menggunakan 1.8.6 dan 1.8.7 (ya beberapa dari kita masih menggunakannya) daripada harus menggunakan hal-hal aneh seperti each_with_index.mapdll. Dan bahkan kita yang pada versi yang lebih baru mungkin lebih suka daripada harus menggunakan map.with_index FWIW :)
rogerdpack

1

Saya sering melakukan ini:

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

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

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

Alih-alih secara langsung mengulangi elemen-elemen array, Anda melakukan iterasi pada berbagai bilangan bulat dan menggunakannya sebagai indeks untuk mengambil elemen-elemen array.


1
Jika Anda membaca jawaban lain, saya harap Anda sekarang menyadari ada pendekatan yang lebih baik. Jadi tidak yakin mengapa Anda perlu menambahkan ini.
nathanvda

Jika jawaban Andrew Grimm layak mendapat sepuluh suara, maka jawaban ini pantas setidaknya satu!
Camille Goudeseune
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.