Jak mapować z indeksem w Ruby?


438

Jaki jest najprostszy sposób na konwersję

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

do

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

Odpowiedzi:


835

Jeśli używasz Ruby 1.8.7 lub 1.9, możesz skorzystać z faktu, że metody iteratora, takie jak each_with_index, gdy są wywoływane bez bloku, zwracają Enumeratorobiekt, który możesz wywoływać Enumerablemetodami jak mapna. Możesz więc zrobić:

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

W 1.8.6 możesz:

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

Dzięki! Czy możesz podać mi wskaźnik do dokumentacji .each_with_index.map?
— Misha Moroshko

1
@Misha: mapjest metodą Enumerablejak zawsze. each_with_index, Gdy wywołana bez bloku, zwraca Enumeratorobiekt (w 1.8.7+), który miesza w Enumerable, tak można nazwać map, select, rejectitp na nim tak jak na tablicy, hash, zasięg itd
— sepp2k

9
IMO jest to prostsze i lepiej czyta w 1.8.7+:arr.map.with_index{ |o,i| [o,i+2] }
— Phrogz

4
@Progrog: map.with_indexnie działa w 1.8.7 ( mapzwraca tablicę, gdy jest wywoływany bez bloku w 1.8).
— sepp2k 15.01.11

2
Ważne, aby pamiętać, że to nie działa z .map! jeśli chcesz bezpośrednio wpłynąć na tablicę, na której zapętlasz.
— Ash Blue,


130

W Ruby 1.9.3 istnieje metoda with_indexłańcuchowa, którą można powiązać z mapą.

Na przykład:

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

17

Over the top zaciemnianie:

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

12
Andrew musi mieć świetne bezpieczeństwo pracy! :)
— David J.,

9

Oto dwie dodatkowe opcje 1.8.6 (lub 1.9) bez użycia modułu wyliczającego:

# 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

Zabawny, ale bezużyteczny sposób:

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

Dlaczego nienawiść? Jest to funkcjonalny sposób na zrobienie tego ORAZ nawet mówię, że jest to głupi sposób na osiągnięcie rezultatów.
— Automatico,

wywołanie do #index oznacza, że ​​jest to teraz pętla O (N ^ 2), także dlaczego +2? :)
— rogerdpack,

2
Kiedy piszę A fun, but useless way. +2jest utworzenie wyniku, o który prosi 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
O MÓJ BOŻE! Czy w ogóle czytałeś inne odpowiedzi? map.with_indexjuż istnieje w rubinie. Po co sugerować, aby ponownie otworzyć wyliczalną klasę i dodać coś, co już istnieje?
— nathanvda

Może to być łatwiejszy sposób na skorzystanie z wersji 1.8.6 i 1.8.7 (tak, niektórzy z nas nadal go używają) zamiast konieczności używania dziwniejszych rzeczy, takich jak each_with_index.mapitp., A nawet ci z nowszych wersji woleliby to niż używać map.with_index FWIW :)
— rogerdpack

1

Często to robię:

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

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

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

Zamiast iterować bezpośrednio po elementach tablicy, iterujesz w zakresie liczb całkowitych i używasz ich jako wskaźników do pobierania elementów tablicy.


1
Jeśli przeczytasz inne odpowiedzi, mam nadzieję, że teraz zdajesz sobie sprawę, że istnieją lepsze podejścia. Nie jestem pewien, dlaczego musiałeś to dodać.
— nathanvda

Jeśli odpowiedź Andrew Grimma zasługuje na dziesięć głosów, to ta zasługuje na co najmniej jeden!
— Camille Goudeseune,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.