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.