Ruby each_with_index offset

84

Posso definir o deslocamento do índice no iterador de loop each_with_index? Minha tentativa direta falhou:

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

Editar:

Esclarecimento: Não quero um deslocamento de array. Quero que o índice dentro de each_with_index não comece em 0, mas, por exemplo, em 1

Marca
fonte
qual versão de Ruby você usa?
fl00r
Desculpe por não escrever, mas eu uso Ruby 1.9.2
Marcos

Respostas:

110

Na verdade, Enumerator#with_indexrecebe deslocamento como um parâmetro opcional:

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

saídas:

1: foo
2: bar
3: baz

BTW, acho que só existe no 1.9.2.

Mladen Jablanović
fonte
2
em 1.8.7 é apenas with_indexsem parâmetros, índices de0
mpapis de
na verdade, uma resposta ainda mais curta é possível, veja a minha abaixo.
Zack Xu
50

O seguinte é sucinto, usando a classe Enumerator do Ruby.

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

resultado

1: foo
2: bar
3: baz

Array # each retorna um enumerador e chamar Enumerator # with_index retorna outro enumerador, para o qual um bloco é passado.

Zack Xu
fonte
5

1) O mais simples é substituir em index+1vez de indexpara a função:

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

mas provavelmente não é isso que você deseja.

2) A próxima coisa que você pode fazer é definir um índice diferente jdentro do bloco e usá-lo em vez do índice original:

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

3) Se você deseja usar o índice dessa forma com frequência, defina outro método:

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


Atualizar

Essa resposta, que foi respondida há alguns anos, agora é obsoleta. Para Rubis modernos, a resposta de Zack Xu funcionará melhor.

Sawa
fonte
o ruim é que ele irá itarar mesmo depois que não houver mais elementos na matriz
fl00r
@ fl00r Sério? No meu exemplo, ele para após três.
sawa
Mas se o deslocamento for 2 ou 10? No seu caso, o deslocamento é zero. Quero dizer, aqui não há qualquer deslocamento em seu (3)
fl00r
@ fl00r Você acabou de alterar o +1no meu código para +2ou +10. Funciona também.
sawa
OMG, o autor editou sua postagem, então ele precisa do deslocamento do índice, não da matriz.
fl00r
4

Se some_indexfor de alguma forma significativo, considere usar um hash, em vez de uma matriz.

Andrew Grimm
fonte
4

Eu corri para ele.

Minha solução desnecessária é a melhor, mas funcionou para mim.

Na iteração de visualização:

basta adicionar: índice + 1

Isso é tudo para mim, já que não uso nenhuma referência a esses números de índice, mas apenas para mostrar em uma lista.

Ariel De La Rosa
fonte
3

Sim você pode

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

Também devo notar que se o deslocamento for maior do que o tamanho do seu Array, ocorrerá um erro. Porque:

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

O que podemos fazer aqui:

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

Ou para pré-validar o deslocamento:

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

Isso é um pouco hacky

UPD 2

Até onde você atualizou sua pergunta e agora você não precisa do deslocamento de matriz, mas de deslocamento de índice, então a solução @sawa funcionará bem para você

fl00r
fonte
1

Ariel está certa. Esta é a melhor maneira de lidar com isso, e não é tão ruim

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

Isso é perfeitamente aceitável e melhor do que a maioria das soluções que vi para isso. Sempre achei que #inject era para isso ... bem.

boulder_ruby
fonte
1

Outra abordagem é usar 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
fonte
1

Isso funciona em todas as versões do Ruby:

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

E para uma matriz genérica:

a.zip(1..a.length.each do |value, index|
  puts value, index
end
fotanus
fonte
faltando um colchete no segundo exemplo.
waferthin
0
offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
ipsum
fonte