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]]
Misha Moroshko
sumber

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] }
sepp2k
sumber
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
258

Ruby memiliki Enumerator # with_index (offset = 0) , jadi pertama-tama konversikan array ke enumerator menggunakan Object # to_enum atau Array # map :

[:a, :b, :c].map.with_index(2).to_a
#=> [[:a, 2], [:b, 3], [:c, 4]]
Tokland
sumber
11
Saya percaya ini adalah jawaban yang lebih baik, karena ini akan bekerja dengan peta! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Connor McKay
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| ... }
fruqi
sumber
17

Kebingungan atas:

arr = ('a'..'g').to_a
indexes = arr.each_index.map(&2.method(:+))
arr.zip(indexes)
Andrew Grimm
sumber
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]]
Phrogz
sumber
8

Saya selalu menikmati sintaks gaya ini:

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

Memanggil each_with_indexmembuat Anda seorang enumerator yang dapat dengan mudah Anda petakan dengan indeks Anda.

yburyug
sumber
4
bagaimana ini berbeda dari jawabannya, diberikan hampir 5 tahun sebelum jawaban Anda ?
Andrey Deineko
4

Cara yang menyenangkan, tetapi tidak berguna untuk melakukan ini:

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}
Otomatis
sumber
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
3
a = [1, 2, 3]
p [a, (2...a.size+2).to_a].transpose
Nikolay Bobrovskiy
sumber
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]]
Mo Wad
sumber
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.

grandinero
sumber
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