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
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.
5.downto(1) do |i|
puts i
end
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.
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.
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
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!
array.reverse.each {|index| … }
“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.)