88

Can I define the offset of the index in the each_with_index loop iterator? My straight forward attempt failed:

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

Edit:

Clarification: I don't want an array offset I want that the index within the each_with_index doesn't start from 0 but e.g. 1.

2
  • what Ruby version do you use? Commented Apr 13, 2011 at 10:06
  • Sorry for not writing, but I use Ruby 1.9.2 Commented Apr 13, 2011 at 10:54

10 Answers 10

111

Actually, Enumerator#with_index receives offset as an optional parameter:

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

outputs:

1: foo
2: bar
3: baz

BTW, I think it is there only in 1.9.2.

Sign up to request clarification or add additional context in comments.

2 Comments

in 1.8.7 it's only with_index no params, indexes from 0
actually, an even shorter answer is possible, please see mine below.
54

The following is succinct, using Ruby's Enumerator class.

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

output

1: foo
2: bar
3: baz

Array#each returns an enumerator, and calling Enumerator#with_index returns another enumerator, to which a block is passed.

Comments

5

1) The simplest is to substitute index+1 instead of index to the function:

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

but probably that is not what you want.

2) The next thing you can do is to define a different index j within the block and use it instead of the original index:

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

3) If you want to use index in this way often, then define another method:

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


Update

This answer, which was answered a few years ago, is now obsolete. For modern Rubies, Zack Xu's answer will work better.

7 Comments

the bad thing that it will itarate even after there is no more elements in array
@fl00r Really? In my example, it stops after three.
But if offset is 2 or 10? In your case offset is zero. I mean here is no any offset in your (3)
@fl00r You just change the +1 in my code to +2 or +10. Its works as well.
OMG, author edited his post, so he need index offset not the array.
|
4

If some_index is somehow meaningful, then consider using a hash, rather than an array.

Comments

4

I ran into it.

My solution not necessary is the best, but it just worked for me.

In the view iteration:

just add: index + 1

That's all for me, as I don't use any reference to those index numbers but just for show in a list.

Comments

3

Yes, you can

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

Also I should notice that if offset is more than your Array size it will though an error. Because:

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

What can we do here:

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

Or to prevalidate offset:

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

This is little hacky

UPD 2

As far as you updated your question and now you need not Array offset, but index offset so @sawa solution will works fine for you

Comments

2

This works in every ruby version:

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

And for a generic array:

a.zip(1..a.length.each do |value, index|
  puts value, index
end

1 Comment

missing a bracket in the second example.
1

Ariel is right. This is the best way to handle this, and it's not that bad

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

That is perfectly acceptable, and better than most of the solutions I've seen for this. I always thought this was what #inject was for...oh well.

Comments

1

Another approach is to use 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) }

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.