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]]
Misza Moroszko
źródło

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] }
sepp2k
źródło
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,
258

Ruby ma Enumerator # with_index (offset = 0) , więc najpierw przekonwertuj tablicę na enumerator za pomocą Object # to_enum lub Array # map :

[:a, :b, :c].map.with_index(2).to_a
#=> [[:a, 2], [:b, 3], [:c, 4]]
tokland
źródło
11
Wierzę, że to lepsza odpowiedź, ponieważ będzie działać z mapą! foo = ['d'] * 5; foo.map!.with_index { |x,i| x * i }; foo #=> ["", "d", "dd", "ddd", "dddd"]
Connor McKay
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| ... }
fruqi
źródło
17

Over the top zaciemnianie:

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

Zawsze podobała mi się składnia tego stylu:

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

Wywołanie each_with_indexdaje ci moduł wyliczający, który możesz łatwo zmapować z dostępnym indeksem.

yburyug
źródło
4
jak to różni się od odpowiedzi udzielonej prawie 5 lat przed twoją ?
Andrey Deineko
4

Zabawny, ale bezużyteczny sposób:

az  = ('a'..'z').to_a
azz = az.map{|e| [e, az.index(e)+2]}
Automatico
źródło
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
3
a = [1, 2, 3]
p [a, (2...a.size+2).to_a].transpose
Nikołaj Bobrowski
źródło
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
źródło
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.

grandinero
źródło
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,