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 будет отлично работать для вас


1

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

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

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


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) }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.