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.


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.


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é.


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.


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.


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.


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


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.


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) }

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

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) }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.