Ruby offset each_with_index

84

Czy mogę zdefiniować przesunięcie indeksu w iteratorze pętli each_with_index? Moja prosta próba nie powiodła się:

some_array.each_with_index{|item, index = 1| some_func(item, index) }

Edytować:

Wyjaśnienie: nie chcę przesunięcia tablicy Chcę, aby indeks w ramach each_with_index nie zaczynał się od 0, ale np. 1.

znak
źródło
jakiej wersji Ruby używasz?
fl00r
Przepraszam, że nie piszę, ale używam Rubiego 1.9.2
Mark

Odpowiedzi:

110

Właściwie Enumerator#with_indexotrzymuje przesunięcie jako opcjonalny parametr:

[:foo, :bar, :baz].to_enum.with_index(1).each do |elem, i|
  puts "#{i}: #{elem}"
end

wyjścia:

1: foo
2: bar
3: baz

Swoją drogą myślę, że jest tylko w 1.9.2.

Mladen Jablanović
źródło
2
w 1.8.7 to tylko with_indexbrak parametrów, indeksy z0
mpapis
w rzeczywistości możliwa jest jeszcze krótsza odpowiedź, zobacz moją poniżej.
Zack Xu
50

Poniższy tekst jest zwięzły, używając klasy modułu wyliczającego Rubiego.

[:foo, :bar, :baz].each.with_index(1) do |elem, i|
    puts "#{i}: #{elem}"
end

wynik

1: foo
2: bar
3: baz

Array # każdy zwraca moduł wyliczający, a wywołanie Enumerator # with_index zwraca inny moduł wyliczający, do którego przekazywany jest blok.

Zack Xu
źródło
5

1) Najprościej jest podstawić index+1zamiast indexfunkcji:

some_array.each_with_index{|item, index| some_func(item, index+1)}

ale prawdopodobnie nie tego chcesz.

2) Następną rzeczą, jaką możesz zrobić, jest zdefiniowanie innego indeksu jw bloku i użycie go zamiast oryginalnego indeksu:

some_array.each_with_index{|item, i| j = i + 1; some_func(item, j)}

3) Jeśli chcesz często używać indeksu w ten sposób, zdefiniuj inną metodę:

module Enumerable
  def each_with_index_from_one *args, &pr
    each_with_index(*args){|obj, i| pr.call(obj, i+1)}
  end
end

%w(one two three).each_with_index_from_one{|w, i| puts "#{i}. #{w}"}
# =>
1. one
2. two
3. three


Aktualizacja

Ta odpowiedź, na którą odpowiedziano kilka lat temu, jest już nieaktualna. W przypadku nowoczesnych rubinów odpowiedź Zacka Xu będzie działać lepiej.

sawa
źródło
zła rzecz, że będzie itarować nawet wtedy, gdy nie będzie już elementów w tablicy
fl00r
@ fl00r Naprawdę? W moim przykładzie zatrzymuje się po trzeciej.
sawa
Ale jeśli przesunięcie wynosi 2 lub 10? W twoim przypadku offset wynosi zero. Mam na myśli, że tutaj nie ma żadnego przesunięcia w twoim (3)
fl00r
@ fl00r Po prostu zmień +1w moim kodzie na +2lub +10. To też działa.
sawa
OMG, autor zredagował swój post, więc potrzebuje przesunięcia indeksu, a nie tablicy.
fl00r
4

Jeśli some_indexjest w jakiś sposób sensowne, rozważ użycie skrótu zamiast tablicy.

Andrew Grimm
źródło
4

Wpadłem na to.

Moje rozwiązanie niekonieczne jest najlepsze, ale po prostu zadziałało.

W widoku iteracja:

po prostu dodaj: indeks + 1

To wszystko dla mnie, ponieważ nie używam żadnych odniesień do tych numerów indeksu, ale tylko po to, aby pokazać je na liście.

Ariel De La Rosa
źródło
3

Tak, możesz

some_array[offset..-1].each_with_index{|item, index| some_func(item, index) }
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
some_array[offset..-1].each_with_index{|item, index| index+=offset; some_func(item, index) }

UPD

Powinienem również zauważyć, że jeśli przesunięcie jest większe niż rozmiar twojej tablicy, będzie to błąd. Dlatego:

some_array[1000,-1] => nil
nil.each_with_index => Error 'undefined method `each_with_index' for nil:NilClass'

Co możemy tu zrobić:

 (some_array[offset..-1]||[]).each_with_index{|item, index| some_func(item, index) }

Lub aby wstępnie zweryfikować przesunięcie:

 offset = 1000
 some_array[offset..-1].each_with_index{|item, index| some_func(item, index) } if offset <= some_array.size

To trochę hacky

UPD 2

O ile zaktualizowałeś swoje pytanie i teraz nie potrzebujesz przesunięcia tablicy, ale przesunięcia indeksu, więc rozwiązanie @sawa będzie działać dobrze dla Ciebie

fl00r
źródło
1

Ariel ma rację. To najlepszy sposób, aby sobie z tym poradzić i nie jest tak źle

ary.each_with_index do |a, i|
  puts i + 1
  #other code
end

Jest to całkowicie akceptowalne i lepsze niż większość rozwiązań, które widziałem. Zawsze myślałem, że właśnie po to jest #inject ... no cóż.

boulder_ruby
źródło
1

Innym podejściem jest użycie map

some_array = [:foo, :bar, :baz]
some_array_plus_offset_index = some_array.each_with_index.map {|item, i| [item, i + 1]}
some_array_plus_offset_index.each{|item, offset_index| some_func(item, offset_index) }
Andrew Grimm
źródło
1

Działa to w każdej wersji ruby:

%W(one two three).zip(1..3).each do |value, index|
  puts value, index
end

A dla ogólnej tablicy:

a.zip(1..a.length.each do |value, index|
  puts value, index
end
fotanus
źródło
brak nawiasu w drugim przykładzie.
waferthin
0
offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
ipsum
źródło