Ruby each_with_index offset

84

Kann ich den Offset des Index im Schleifeniterator each_with_index definieren? Mein direkter Versuch schlug fehl:

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

Bearbeiten:

Erläuterung: Ich möchte keinen Array-Offset. Ich möchte, dass der Index im each_with_index nicht bei 0 beginnt, sondern bei 1.

Kennzeichen
quelle
Welche Ruby-Version verwenden Sie?
fl00r
Tut mir leid, dass ich nicht geschrieben habe, aber ich benutze Ruby 1.9.2
Mark

Antworten:

110

Tatsächlich Enumerator#with_indexerhält Offset als optionalen Parameter:

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

Ausgänge:

1: foo
2: bar
3: baz

Übrigens denke ich, dass es nur in 1.9.2 da ist.

Mladen Jablanović
quelle
2
in 1.8.7 ist es nur with_indexkeine params, Indizes von0
mpapis
Tatsächlich ist eine noch kürzere Antwort möglich, siehe meine unten.
Zack Xu
49

Das Folgende ist kurz und bündig mit Rubys Enumerator-Klasse.

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

Ausgabe

1: foo
2: bar
3: baz

Array # gibt jeweils einen Enumerator zurück, und der Aufruf von Enumerator # with_index gibt einen anderen Enumerator zurück, an den ein Block übergeben wird.

Zack Xu
quelle
5

1) Am einfachsten ist es, index+1anstelle indexder Funktion Folgendes zu ersetzen :

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

aber wahrscheinlich ist das nicht was du willst.

2) Als Nächstes können Sie einen anderen Index jinnerhalb des Blocks definieren und anstelle des ursprünglichen Index verwenden:

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

3) Wenn Sie den Index häufig auf diese Weise verwenden möchten, definieren Sie eine andere Methode:

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


Aktualisieren

Diese Antwort, die vor einigen Jahren beantwortet wurde, ist jetzt veraltet. Für moderne Rubine funktioniert die Antwort von Zack Xu besser.

sawa
quelle
das schlimme, dass es itarate wird, selbst wenn es keine Elemente mehr im Array gibt
fl00r
@ fl00r Wirklich? In meinem Beispiel hört es nach drei auf.
Sawa
Aber wenn der Versatz 2 oder 10 ist? In Ihrem Fall ist der Offset Null. Ich meine, hier ist kein Versatz in Ihrem (3)
fl00r
@ fl00r Du änderst einfach den +1in meinem Code auf +2oder +10. Es funktioniert auch.
Sawa
OMG, der Autor hat seinen Beitrag bearbeitet, sodass er den Indexversatz und nicht das Array benötigt.
fl00r
4

Wenn dies some_indexirgendwie sinnvoll ist, sollten Sie einen Hash anstelle eines Arrays verwenden.

Andrew Grimm
quelle
4

Ich bin darauf gestoßen.

Meine nicht notwendige Lösung ist die beste, aber sie hat nur bei mir funktioniert.

In der Ansichtsiteration:

füge einfach hinzu: index + 1

Das ist alles für mich, da ich keinen Verweis auf diese Indexnummern verwende, sondern nur zum Anzeigen in einer Liste.

Ariel De La Rosa
quelle
3

Ja, du kannst

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

Außerdem sollte ich beachten, dass ein Offset, der größer als Ihre Array-Größe ist, einen Fehler verursacht. Weil:

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

Was können wir hier tun:

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

Oder um Offset zu vorherrschen:

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

Das ist ein bisschen hacky

UPD 2

Soweit Sie Ihre Frage aktualisiert haben und jetzt keinen Array-Offset, sondern einen Index-Offset benötigen, funktioniert die @ sawa-Lösung für Sie einwandfrei

fl00r
quelle
1

Ariel hat recht. Dies ist der beste Weg, um damit umzugehen, und es ist nicht so schlimm

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

Das ist völlig akzeptabel und besser als die meisten Lösungen, die ich dafür gesehen habe. Ich dachte immer, das ist es, wofür #inject ist ... na ja.

boulder_ruby
quelle
1

Ein anderer Ansatz ist zu verwenden 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
quelle
1

Dies funktioniert in jeder Ruby-Version:

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

Und für ein generisches Array:

a.zip(1..a.length.each do |value, index|
  puts value, index
end
fotanus
quelle
im zweiten Beispiel fehlt eine Klammer.
Waferthin
0
offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
ipsum
quelle