Ruby each_with_index смещение

84

Могу ли я определить смещение индекса в итераторе цикла each_with_index? Моя прямая попытка не удалась:

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

Редактировать:

Уточнение: мне не нужно смещение массива. Я хочу, чтобы индекс в each_with_index начинался не с 0, а, например, с 1.

отметка
источник
какую версию Ruby вы используете?
fl00r
Извините, что не пишу, но я использую Ruby 1.9.2
Марк

Ответы:

110

Фактически, Enumerator#with_indexполучает смещение как необязательный параметр:

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

выходы:

1: foo
2: bar
3: baz

Кстати, думаю, что только в 1.9.2.

Младен Ябланович
источник
2
в 1.8.7 нет только with_indexпараметров, индексов из0
mpapis 08
на самом деле возможен даже более короткий ответ, см. мой ниже.
Зак Сюй,
50

Ниже приводится краткое описание с использованием класса Ruby Enumerator.

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

вывод

1: foo
2: bar
3: baz

Каждый массив # возвращает перечислитель, а вызов Enumerator # with_index возвращает другой перечислитель, которому передается блок.

Зак Сюй
источник
5

1) Проще всего index+1вместо indexфункции подставить :

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

но, вероятно, это не то, что вам нужно.

2) Следующее, что вы можете сделать, это определить другой индекс jв блоке и использовать его вместо исходного индекса:

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

3) Если вы хотите часто использовать индекс таким образом, определите другой метод:

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


Обновить

Этот ответ, на который был дан ответ несколько лет назад, теперь устарел. Для современных Рубинов ответ Зака ​​Сю будет работать лучше.

сава
источник
плохо, что он будет продолжать работать даже после того, как в массиве больше нет элементов
fl00r
@ fl00r Правда? В моем примере он останавливается через три.
sawa
Но если смещение 2 или 10? В вашем случае смещение равно нулю. Я имею в виду, что здесь нет никакого смещения в вашем (3)
fl00r
@ fl00r Просто измените +1в моем коде на +2или +10. Это тоже работает.
sawa
OMG, автор отредактировал свой пост, поэтому ему нужно смещение индекса, а не массива.
fl00r
4

Если some_indexэто имеет какое-то значение, рассмотрите возможность использования хеша, а не массива.

Эндрю Гримм
источник
4

Я столкнулся с этим.

Мое решение не является лучшим, но оно сработало для меня.

В итерации просмотра:

просто добавьте: index + 1

На этом все для меня, поскольку я не использую никаких ссылок на эти номера индексов, а просто для отображения в списке.

Ариэль Де ла Роса
источник
3

Да, ты можешь

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

Также я должен заметить, что если смещение больше, чем размер вашего массива, это произойдет с ошибкой. Потому как:

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

Что мы можем здесь сделать:

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

Или для предварительной проверки смещения:

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

Это немного взломано

UPD 2

Насколько вы обновили свой вопрос, и теперь вам нужно не смещение массива, а смещение индекса, поэтому решение @sawa будет отлично работать для вас

fl00r
источник
1

Ариэль права. Это лучший способ справиться с этим, и это не так уж плохо

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

Это вполне приемлемо и лучше, чем большинство решений, которые я видел для этого. Я всегда думал, что это то, для чего нужен #inject ... да ладно.

boulder_ruby
источник
1

Другой подход - использовать 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

Это работает в любой версии Ruby:

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

И для общего массива:

a.zip(1..a.length.each do |value, index|
  puts value, index
end
фотанус
источник
отсутствует скобка во втором примере.
waferthin
0
offset = 2
some_array[offset..-1].each_with_index{|item, index| some_func(item, index+offset) }
ipsum
источник