Mathew
Mathew

Reputation: 231

Can anyone explain this Ruby method to me?

Can anyone explain to me this method? If I send an index as 4, what does the code return actually?.

def getOffset(index)
    @baseIndex = 0 if @baseIndex.nil?
    raise IndexError if not \
           (@baseIndex ... @baseIndex + length) === index
    return index - @baseIndex
end

I am not able to understand the part after the first line.

Upvotes: 0

Views: 101

Answers (4)

dax
dax

Reputation: 10997

# this sets the base index to 0 unless `@baseIndex` is defined
@baseIndex = 0 if @baseIndex.nil? 

# this raises an error if the index you passed in is larger than the size of your array
raise IndexError if not (@baseIndex ... @baseIndex + length) === index 

# this returns the offset
return index - @baseIndex

So, if you did not set @baseIndex and passed 4, this method would return 4.
If you set @baseIndex to 2, this method would return 2.
If you set @baseIndex to 5, this method would return an IndexError.

Upvotes: 1

pangpang
pangpang

Reputation: 8821

=== is used to test equality within a when clause of a case statement.

For example:

(1...10) === 5 returns true.
(1...10) === 11 returns false.

Upvotes: 1

engineersmnky
engineersmnky

Reputation: 29318

You could also refactor the code a bit for readability such as

def get_offset_refactor(index)
  @base_index ||= 0
  (@base_index...@base_index + length).include?(index) ? index - @base_index : raise IndexError 
end

This reads as

#if base index is not nil use it's value otherwise use 0
@base_index = @base_index || 0
#create a range from @base_index up to be not including @base_index + length
#check if index is included in that range
#if so then return index - @base_index
#otherwise raise an IndexError
(@base_index ... @base_index + length).include?(index) ? index - @base_index : raise IndexError

Example

#because this is not defined anywhere
def length
  5
end
get_offset_refactor(4)

@base_index = @base_index || 0 #@base_index will now = 0 because it was not set 
#next line becomes
(0...(0+5)).include?(4) ? 4 - 0 : raise(IndexError) 
#^[0,1,2,3,4]        ^does this include 4 => yes
#return 4 - 0
#=> 4

If you run it with a 5 then it will raise because [1,2,3,4] does not include 5.

Hope this helps you understand this method is highly reliant on one instance variable @base_index and 1 method/local variable length.

Upvotes: 1

Stefan
Stefan

Reputation: 114158

Cleaning up your code

Your method's second line looks pretty confusing, let's refactor it:

raise IndexError if not \
       (@baseIndex ... @baseIndex + length) === index

The above is actually a single line:

raise IndexError if not (@baseIndex ... @baseIndex + length) === index

The case equality operator Range#=== should not be called explicitly. Use include? instead, which does the same:

raise IndexError if not (@baseIndex ... @baseIndex + length).include?(index)

Finally, if not can be written as unless and some spaces can be removed:

raise IndexError unless (@baseIndex...@baseIndex + length).include?(index)

Better.

What does it do?

Let's assume @baseIndex = 4 and length = 10:

raise IndexError unless (@baseIndex...@baseIndex + length).include?(index)
# is evaluated as:
raise IndexError unless (4...4 + 10).include?(index)
# is evaluated as:
raise IndexError unless (4...14).include?(index)

It raises an IndexError unless index is between 4 and 14 (excluding 14). Otherwise, it continues with the next line and returns index - @baseIndex.

What else?

  • use snake_case for method names and variables (@base_index)
  • don't write return unless necessary
  • avoid get and set prefixes, i.e. def offset is usually enough
  • initialize @base_index in your initializer (not in your offset method):

    def initialize
      @base_index = 0
    end
    
  • extract the range calculation:

    def valid_range
      @base_index...@base_index + length
    end
    
    def offset(index)
      raise IndexError unless valid_range.include?(index)
      index - @base_index
    end
    

Upvotes: 5

Related Questions