Ruby each_with_index offset

84

Puis-je définir le décalage de l'index dans l'itérateur de boucle each_with_index? Ma simple tentative a échoué:

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

Éditer:

Clarification: je ne veux pas de décalage de tableau.Je veux que l'index dans each_with_index ne commence pas à 0 mais par exemple à 1.

marque
la source
quelle version de Ruby utilisez-vous?
fl00r
Désolé de ne pas écrire, mais j'utilise Ruby 1.9.2
Mark

Réponses:

110

En fait, Enumerator#with_indexreçoit le décalage en tant que paramètre facultatif:

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

les sorties:

1: foo
2: bar
3: baz

BTW, je pense que ce n'est là que dans 1.9.2.

Mladen Jablanović
la source
2
dans 1.8.7 il with_indexn'y a que pas de paramètres, index de0
mpapis
en fait, une réponse encore plus courte est possible, veuillez voir la mienne ci-dessous.
Zack Xu
50

Ce qui suit est succinct, en utilisant la classe Enumerator de Ruby.

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

production

1: foo
2: bar
3: baz

Array # renvoie chacun un énumérateur et l'appel de Enumerator # with_index renvoie un autre énumérateur, auquel un bloc est passé.

Zack Xu
la source
5

1) Le plus simple est de remplacer à la index+1place de indexla fonction:

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

mais ce n'est probablement pas ce que vous voulez.

2) La prochaine chose que vous pouvez faire est de définir un index différent jdans le bloc et de l'utiliser à la place de l'index d'origine:

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

3) Si vous souhaitez utiliser souvent l'index de cette manière, définissez une autre méthode:

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


Mise à jour

Cette réponse, à laquelle on a répondu il y a quelques années, est désormais obsolète. Pour les Rubis modernes, la réponse de Zack Xu fonctionnera mieux.

Sawa
la source
la mauvaise chose qu'il va fonctionner même après qu'il n'y ait plus d'éléments dans le tableau
fl00r
@ fl00r Vraiment? Dans mon exemple, il s'arrête après trois heures.
sawa
Mais si le décalage est de 2 ou 10? Dans votre cas, le décalage est égal à zéro. Je veux dire qu'il n'y a pas de décalage dans votre (3)
fl00r
@ fl00r Vous venez de changer le +1dans mon code en +2ou +10. Cela fonctionne aussi.
sawa
OMG, l'auteur a édité son message, il a donc besoin d'un décalage d'index et non du tableau.
fl00r
4

Si cela a un some_indexsens, envisagez d'utiliser un hachage plutôt qu'un tableau.

Andrew Grimm
la source
4

Je suis tombé dessus.

Ma solution n'est pas nécessaire est la meilleure, mais cela a fonctionné pour moi.

Dans l'itération de vue:

il suffit d'ajouter: index + 1

C'est tout pour moi, car je n'utilise aucune référence à ces numéros d'index, mais juste pour les afficher dans une liste.

Ariel De La Rosa
la source
3

Oui, vous pouvez

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

Je dois également remarquer que si le décalage est supérieur à la taille de votre tableau, il y aura une erreur. Car:

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

Que pouvons-nous faire ici:

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

Ou pour prévalider l'offset:

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

C'est un peu hacky

UPD 2

Dans la mesure où vous avez mis à jour votre question et que vous n'avez plus besoin de décalage de tableau, mais de décalage d'index, la solution @sawa fonctionnera bien pour vous

fl00r
la source
1

Ariel a raison. C'est la meilleure façon de gérer cela, et ce n'est pas si mal

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

C'est parfaitement acceptable et meilleur que la plupart des solutions que j'ai vues pour cela. J'ai toujours pensé que c'était à ça que servait #inject ... eh bien.

boulder_ruby
la source
1

Une autre approche consiste à utiliser 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
la source
1

Cela fonctionne dans toutes les versions de rubis:

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

Et pour un tableau générique:

a.zip(1..a.length.each do |value, index|
  puts value, index
end
fotanus
la source
il manque un crochet dans le deuxième exemple.
waferthin
0
offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
ipsum
la source