rudolph9
rudolph9

Reputation: 8129

Reverse `...` method Ruby

Is there a standard method in ruby similar to (1...4).to_a is [1,2,3,4] except reverse i.e. (4...1).to_a would be [4,3,2,1]?

I realize this can easily be defined via (1...4).to_a.reverse but it strikes me as odd that it is not already and 1) am I missing something? 2) if not, is there a functional/practical reason it is not already?

Upvotes: 3

Views: 1508

Answers (3)

Michael Slade
Michael Slade

Reputation: 13877

(1...4) is a Range. Ranges in ruby are not like arrays; one if their advantages is you can create a range like

(1..1e9)

without taking up all of your machine's memory. Also, you can create this range:

r = (1.0...4.0)

Which means "the set of all floating point numbers from 1.0 to 4.0, including 1.0 but not 4.0"

In other words:

irb(main):013:0> r.include? 3.9999
=> true
irb(main):014:0> r.include? 3.99999999999
=> true
irb(main):015:0> r.include? 4.0
=> false

you can turn an integer Range into an array:

irb(main):022:0> (1..4).to_a
=> [1, 2, 3, 4]

but not a floating point range:

irb(main):023:0> (1.0...4.0).to_a
TypeError: can't iterate from Float
        from (irb):23:in `each'
        from (irb):23:in `to_a'
        from (irb):23
        from /home/mslade/rubygems1.9/bin/irb:12:in `<main>'

Because there is no natural way to iterate over floating point numbers. Instead you use #step:

irb(main):015:0> (1..4).step(0.5).to_a
=> [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0]
irb(main):016:0> (1...4).step(0.5).to_a
=> [1.0, 1.5, 2.0, 2.5, 3.0, 3.5]

If you need to iterate backwards through a large integer range, use Integer#downto.

Upvotes: 8

Michael Kohl
Michael Kohl

Reputation: 66867

The easiest is probably this:

4.downto(1).to_a #=> [4, 3, 2, 1]

Alternatively you can use step:

4.step(1,-1).to_a #=> [4, 3, 2, 1]

Finally a rather obscure solution for fun:

(-4..-1).map(&:abs) #=> [4, 3, 2, 1]

Upvotes: 9

joelparkerhenderson
joelparkerhenderson

Reputation: 35483

You could patch Range#to_a to automatically work with reverse like this:

class Range

  alias :to_a_original :to_a

  def reverse
    Range.new(last, first)
  end

  def to_a
    (first < last) ? to_a_original : reverse.to_a_original.reverse
  end

end

Result:

(4..1).to_a
=> [4, 3, 2, 1]

This approach is called "re-opening" the class a.k.a. "monkey-patching". Some developers like this approach because it's adding helpful functionality, some dislike it because it's messing with Ruby core.)

Upvotes: 1

Related Questions