Looping Backwards in Ruby

It so happens in Ruby that looping forwards is trivial; if you want to print out numbers from one to five, you can do so like this:


(1..5).each do |i|
puts i
end

This will print:

1
2
3
4
5

But how about going backwards? Can you do this?


(5..1).each do |i|
puts i
end

No! This won’t print anything out! Boo!

Edit: The solution is, happily, as easy as:

5.downto(1).do |i|
puts i

end

Props out to Wayne Conrad for mentioning it in the comments! In any case, you can read on to see a discussion of some other solutions to this problem–solutions that will work if you don’t know if you’re going to be counting up or counting down.


Ok, so you might do this, instead:


count = 5
while count > 0
puts count
count -= 1
end

Ok, that’ll work. But what if you have two variables, x and y, and you want to iterate from x and y–except you never know if x is less than y or greater than y? (If x is less than y, you can use the usual (x..y).each do |i|, but if not, what then?)

There are a couple of ways you can tackle this–one is swapping, and the other is using min/max, and we discuss the reverse series method.

Swapping means swapping x and y if x > y, like so:


x = #...
y = #...

if (x > y)
temp = x
x = y
y = temp
end

(x..y).each do #...

This is a great solution. Sometimes, though, it doesn’t work–like in my case, I had an issue where I go from x to y, and then from y to some other value. Swapping just destroys the second part of the process.

Which brings us to solution two–using min/max functions (which, incidentally, don’t come built-in to Ruby). So you would write:


(min(x, y)..max(x, y)).each do #...

And this will iterate properly, without destroying the values of x and y. Of course, you can define min/max as:


def min(a, b)
return b unless a < b
return a
end


def max(a, b)
return b unless a > b
return a
end

And you can use this solution. AND, if you don't know if x is greater than or less than y, then this solution will STILL work! Yay!

BUT! this solution still iterates forward! If that's a problem, you can try solution three, the reverse series. Observe:


steps = y - x
while (steps > 0)
i = y - steps #(y, y-1, y-2 ... x)
# do something for step i
end

This iterates backwards; the value of i is y, then y-1, then y-2, and so on, up to x. (Or, as mentioned before, you can use downto.)

Phew! So that covers a few different ways of iterating backwards; use whatever works for your needs.

About Ashiq Alibhai

Ashiq Alibhai, PMP, has been a Rails aficionado since 2007, and developed web applications since early 2003, where he learned PHP in one summer. As the driving-force behind RailsRocket and the Launchpad project, he seeks to share the ease of development with Rails far and wide.
This entry was posted in Development, Projects. Bookmark the permalink.

7 Responses to Looping Backwards in Ruby

  1. Wayne Conrad says:

    5.downto(1) do |i|
    puts i
    end

  2. Wayne Conrad says:

    Oh, I forgot to mention, and you’ll be glad to know I’m sure, that the min and max functions are there, cleverly hidden in Enumerable. Try [1, 2].max and [1, 2].min for example.

  3. ashes999 says:

    Wow. That just blows my mind. I knew Ruby had to have some sort of solution for it…

    Thanks! The min/max makes my life easier :) but as for the iterating, I still need to use a general solution, because I don’t know if x is less than or more than y.

  4. Wayne Conrad says:

    If you needed an iterator that didn’t care which direction you were iterating, you could always make one. Here’s one clumsy way to do it. There’s probably a way built into Ruby 1.9, for all I know.

    class Range
    def iter(&block)
    if first <= last
    each(&block)
    else
    first.downto(last, &block)
    end
    end
    end

    (1..5).iter do |i|
    puts(i)
    end

    (5..1).iter do |i|
    puts(i)
    end

  5. ashes999 says:

    I actually don’t need to iterate backwards explicitly; the min/max method works for me. Still, it’s good to know the solution, in case it matters someday.

    Cheers!

  6. Don Park says:

    array.reverse.each {|index| … }

  7. Luis Esteban says:

    “What is the “right” way to iterate through a Range in Ruby backwards?”!

    It is nice to do (for the forwards):

    (1..10).each{|i| puts “i=#{i}” }

    but I don’t like to do (for the backwards):

    (1..10).to_a.reverse.each{|i| puts “i=#{i}” }

    Well, I don’t actually mind doing that too much, but when I am teaching going backwards, I want to show my students a nice symmetry (i.e. with minimal difference, e.g. only adding a reverse, or a step -1, but without modifying anything else). You can do (for symmetry):

    (a=*1..10).each{|i| puts “i=#{i}” }
    and
    (a=*1..10).reverse.each{|i| puts “i=#{i}” }
    which I don’t like much, but you can’t do

    (*1..10).each{|i| puts “i=#{i}” }
    (*1..10).reverse.each{|i| puts “i=#{i}” }
    #
    (1..10).step(1){|i| puts “i=#{i}” }
    (1..10).step(-1){|i| puts “i=#{i}” }
    #
    (1..10).each{|i| puts “i=#{i}” }
    (10..1).each{|i| puts “i=#{i}” } # I don’t want this though. It’s dangerous

    You could ultimately do

    class Range
    def each_reverse(&block)
    self.to_a.reverse.each(&block)
    end
    end

    but I want to teach pure Ruby rather than object oriented approaches (just yet). I would like to iterate backwards:

    – without creating an array (consider 0..1000000000)
    – working for any Range (e.g. Strings, not just Integers)
    – without using any extra object oriented power (i.e. no class modification)

    I believe this is impossible without defining a pred method, which means modifying the Range class to use it. If you can do this please let me know, otherwise confirmation of impossibility would be appreciated though it would be disappointing. Perhaps Ruby 1.9 addresses this.

    The “right” way is

    class Range

    def each_reverse(&block)
    i = self.last
    while self === i
    block.call(i)
    i = i.pred
    end
    self
    end

    def every(&block)
    if self.first < self.last
    range = self
    direction = :succ
    else
    range = self.last .. self.first
    direction = :pred
    end
    i = self.first
    while range === i
    block.call(i)
    i = i.send(direction)
    end
    self
    end

    end

    class Integer

    def pred
    self – 1
    end

    end

    class String

    def pred
    return ” if self == ”
    self[0..-2] + (self[-1]-1).chr
    end

    end

    ranges = [
    1..10,
    'a'..'j',
    10..1,
    'j'..'a'
    ]

    ranges.each do |range|
    puts “#{range.inspect}.each …”
    range.each{|i| puts i}
    puts “#{range.inspect}.each_reverse …”
    range.each_reverse{|i| puts i}
    puts “#{range.inspect}.every …”
    range.every{|i| puts i}
    end

    (Thanks for your time in reading this.)