Is there an elegant way to exclude the first value of a range?

I have two suggestions for you, they're not very ideal but they're the best I can think of.

First you can define a new method on the Range class that does what you describe. It'd look like this:

class Range
  def have?(x)
    if x == self.begin
      false
    else
      include?(x)
    end
  end
end

p (0..10).have?(0)       #=> false
p (0..10).have?(0.00001) #=> true

I don't know, I just used a synonym of "include" as a method name, maybe you can think of something better. But that's the idea.

And then you could do something a little more elaborate, and define a method on the Range class that marks a range as one you want to exclude the beginning value of, and then change Range's include? method to check for that mark.

class Range
  def exclude_begin
    @exclude_begin = true
    self
  end

  alias_method :original_include?, :include?
  def include?(x)
    return false if x == self.begin && instance_variable_defined?(:@exclude_begin)
    original_include?(x)
  end

  alias_method :===, :include?
  alias_method :member?, :include?
end

p (0..10).include?(0)                     #=> true
p (0..10).include?(0.00001)               #=> true
p (0..10).exclude_begin.include?(0)       #=> false
p (0..10).exclude_begin.include?(0.00001) #=> true

Again, you may want a better (more elegant?) name for the method than exclude_begin, I just chose that since it's consistent with Range's exclude_end? method.

Edit: I've got another one for you, just because I find this problem so interesting. :P This'll only work in the very latest version of Ruby 1.9, but will allow the following syntax:

(0.exclude..10).include? 0       #=> false
(0.exclude..10).include? 0.00001 #=> true

It uses the same idea as my 2nd suggestion, but stores the "exclusion marker" in the number instead of the range. I have to use Ruby 1.9's SimpleDelegator to accomplish this (numbers on their own can't have instance variables or anything), which is why it won't work in earlier versions of Ruby.

require "delegate"

class Numeric
  def exclude
    o = SimpleDelegator.new(self)
    def o.exclude_this?() true end
    o
  end
end

class Range
  alias_method :original_include?, :include?
  def include?(x)
    return false if x == self.begin &&
                    self.begin.respond_to?(:exclude_this?) &&
                    self.begin.exclude_this?
    original_include?(x)
  end

  alias_method :===, :include?
  alias_method :member?, :include?
end

No.

((0+1)..10)

Tags:

Ruby

Range