user3281384
user3281384

Reputation: 531

Ruby: Iterate over entire array from nth position

I'd like to iterate over an entire array, starting from any position. I'm not sure if there's a way to achieve this easily in Ruby, and I couldn't find any examples in the Array or Enumerator docs.

array = [0, 1, 2, 3, 4]
array.each.starting_at(3) { |e| e }
#=> [3, 4, 0, 1, 2]

And also:

array.each.starting_at_reverse(3) { |e| e }
#=> [3, 2, 1, 0, 4]

Upvotes: 0

Views: 769

Answers (2)

Alex Kalinin
Alex Kalinin

Reputation: 53

You can do this with upto and downto Fixnum's methods:

array = [0, 1, 2, 3, 4]
last_index = array.size - 1

3.upto last_index do |i|
  puts array[i]
end
# => 3, 4

last_index.downto 3 do |i|
  puts array[i]
end
# => 4, 3

PS. as speed benchmark, iteration with rotation faster

array.rotate(3).each {|e| puts e}

benchmark:

require 'benchmark'

array = Array.new(10000000) { rand(1...9) }
last_index = array.size - 1

Benchmark.bm do |x|
  x.report 'upto' do
    10000.upto last_index do |index| a = array[index] + 1; end
  end

  x.report 'downto' do
    last_index.downto 10000 do |index| a = array[index] + 1; end
  end

  x.report 'rotate' do
    array.rotate(10000).each {|e| a = e + 1 }
  end
end

# RESULTS:
# user     system      total        real
# upto    0.680000   0.000000   0.680000 (  0.681932)
# downto  0.680000   0.000000   0.680000 (  0.679752)
# rotate  0.590000   0.040000   0.630000 (  0.622901)

but, as memory benchmark, iteration by array indexes less memory hungry, especially on big array sizes:

require 'memory_profiler'

array = Array.new(10000000) { rand(1...9) }
last_index = array.size - 1

{
  upto: -> { 10000.upto last_index do |index| a = array[index] + 1; end },
  downto: -> { last_index.downto 10000 do |index| a = array[index] + 1; end },
  rotate: -> { array.rotate(10000).each {|e| a = e + 1 } },
  reverse_rotate: -> { array.reverse.rotate(10000).each {|e| a = e + 1 } }
}.each { |desc, code| puts "#{desc.to_s} => #{MemoryProfiler.report(&code).total_allocated_memsize.to_s}" }

# RESULTS (in bytes):
# upto           => 0          # no additional memory allocation
# downto         => 0          # no additional memory allocation
# rotate         => 80000040   # implicitly copied array 1 time
# reverse_rotate => 160000080  # implicitly copied array 2 times

Upvotes: 0

Eli Sadoff
Eli Sadoff

Reputation: 7308

You can use the rotate method for this. This method rotates the position of each element by n. So your examples can be done like this

array.rotate(3).each {|e| e }

and

array.reverse.rotate(1).each {|e| e}

Note: for the second method the parameter to rotate can be derived by finding the negative index of n. So for this the element at index 3 is at index -2 in a length 5 array.

Upvotes: 2

Related Questions