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