Home > Blockchain >  Writing custom method for a hash on Ruby? on rails
Writing custom method for a hash on Ruby? on rails

Time:10-09

i'm trying (and actually succeded, but i don't understand how it works) to write a custom method for a hash in my model (I'm working on Ruby on Rails 6).

My hash looks like this

my_hash = { 
 [['name_1', 'slug_1']=>value_1],
 [['name_2', 'slug_2']=>value_2],
 [['name_1', 'slug_1']=>value_3],
 [['name_2', 'slug_2']=>value_4] 
}

So basically a hash of arrays. You notice that the 'keys' are arrays that repeat themselves many times, but with different values. What i want to achieve is to write a custom method that "joins" all the keys in only one key, which will have an array of values assigned, so basically i should be able to get:

my_hash = { 
 ['name_1', 'slug_1']=>"values": [value_1, value_3],
 ['name_2', 'slug_2']=>"values": [value_2, value_4]
}

For that, I have this piece of code, which i use many times:

my_hash.inject({}) do |hash, record|
  # each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
  keys, value = record
  # now keys has ["unit_name", "axis.slug"] and values equals average_value
  hash[keys.first] ||= {}
  hash[keys.first][keys.last] = value.to_f
  hash
end

Since I use this many times, i wanted to write a custom method, so i did:

  def format_hash_data my_hash
    my_hash.inject({}) do |hash, record|
      # each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
      keys, value = record
      # now keys has ["unit_name", "axis.slug"] and values equals average_value
      hash[keys.first] ||= {}
      hash[keys.first][keys.last] = value.to_f
      hash
    end
  end

And used it like: my_hash = format_hash_data(my_hash) with no success(it threw an error saying that 'format_hash_data' was not a valid method for the class).

So I fiddled around and added 'self' to the name of the method, leaving:

  def self.format_hash_data my_hash
    my_hash.inject({}) do |hash, record|
      # each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
      keys, value = record
      # now keys has ["unit_name", "axis.slug"] and values equals average_value
      hash[keys.first] ||= {}
      hash[keys.first][keys.last] = value.to_f
      hash
    end
  end

Which, to my surprise, worked flawlessly when using my_hash = format_hash_data(my_hash)

I don't really understand why adding 'self' makes my code works, maybe anyone can shed some light? I tried using things like send() or instance_eval first, to just send the piece of code to the actual hash as a method (something like my_hash.instance_eval(my_method)) but I couldn't get it working.

I'm sorry about the long explanation, I hope i was clear enough so any of you who had this same dilemma can understand. Thanks in advance.

CodePudding user response:

Prepending self. to the method name makes it a class method instead of an instance method. If you are not sure of the difference, you should look it up as it is fundamental to properly defining and using classes and methods.

As a class method, you would use it as:

my_hash = MyHash.format_hash_data(my_hash)

Or if you're in scope of the class, simply my_hash = format_hash_data(my_hash), which is why it worked in your case with the self. prepended (class method definition).

If you want to define it as an instance method (a method that is defined for the instance), you would use it like so:

my_hash = my_hash.format_hash_data

And the definition would use the implicit self of the instance:

def format_hash_data
  self.inject({}) do |hash, record|
    # each record has the following format => [["unit_name", "axis.slug"]=>average_value(float)]
    keys, value = record
    # now keys has ["unit_name", "axis.slug"] and values equals average_value
    hash[keys.first] ||= {}
    hash[keys.first][keys.last] = value.to_f
    hash
  end
end
  • Related