The two methods in question don't exist by default, at least not for the objects you're using.
v.include?
would work if v
was an Enumerable, like a hash or an array, but instead it's a numeric value because it's the value for that particular key, and doesn't have that method defined.
The same is true for v.has_value?
. v
is still the value for that particular key. select
passes in the key and value as separate parameters if you use |k,v|
. (In Ruby's way, they'd be a two-element array if you used a single variable.)
To help you learn to help yourself, Ruby includes the ri
command available at the command-line. You can type in ri has_value?
to see if that's implemented by any classes Ri knows about. Among other results is:
=== Implementation from Hash
------------------------------------------------------------------------------
hsh.has_value?(value) -> true or false
------------------------------------------------------------------------------
Returns true if the given value is present for some key in hsh.
h = { "a" => 100, "b" => 200 }
h.has_value?(100) #=> true
h.has_value?(999) #=> false
Along the same lines, for include?
:
=== Implementation from Hash
------------------------------------------------------------------------------
hsh.include?(key) -> true or false
------------------------------------------------------------------------------
Returns true if the given key is present in hsh.
h = { "a" => 100, "b" => 200 }
h.has_key?("a") #=> true
h.has_key?("z") #=> false
In either case, a numeric won't have those methods, which is where the error is coming from.
Would something along the lines of this work?
def my_hash_finding_method(source, thing_to_find)
source.select {|k,v|
if v == thing_to_find then return source[k] end
}
end
That's close, but return
doesn't belong there and will result in only one value, even if there are multiple values existing. Consider this:
MY_FAMILY_PETS_AGES = {
"Evi" => 6,
"Hoobie" => 3,
"George" => 12,
"Bogart" => 4,
"Poly" => 4,
"Annabelle" => 0,
"Ditto" => 3
}
def my_hash_finding_method(source, thing_to_find)
source.select{ |k, v| v == thing_to_find }
end
my_hash_finding_method(MY_FAMILY_PETS_AGES, 3) # => {"Hoobie"=>3, "Ditto"=>3}
If you want to receive sorted results, well, why? select
in current Rubies will return a hash, and hashes don't benefit from sorting since they're basically random-access objects, resulting in the ability to access values just as fast whether the hash is sorted or unsorted.
If you want to return a sorted array of key/value pairs, then this is one way to do it:
def my_hash_finding_method(source, thing_to_find)
source.select{ |k, v| v == thing_to_find }.sort_by{ |k, v| k }
end
my_hash_finding_method(MY_FAMILY_PETS_AGES, 3) # => [["Ditto", 3], ["Hoobie", 3]]
If you only want the names of the pets, instead of name and age, grab only the keys returned by select
:
def my_hash_finding_method(source, thing_to_find)
source.select{ |k, v| v == thing_to_find }.keys.sort
end
my_hash_finding_method(MY_FAMILY_PETS_AGES, 3) # => ["Ditto", "Hoobie"]
Notice that in the solution returning a hash from select
I'm using sort_by
, and a regular sort
in the last one. sort
is fine for values like numerics and characters/strings which already have an established order, however it's inefficient when sorting using a block to access attributes inside the value passed in because it has to do a lot of redundant computing to determine how to order the incoming values. sort_by
is an interesting algorithm commonly known as a Schwartzian Transform, which computes the value being sorted once per entry, remembers it, sorts by that value, then spits out the associated items afterward, in the correct order. That results in a very efficient sorting, which can greatly outperform a regular sort if the value being sorted isn't readily available.
I did benchmarks of the differences and how to quickly perform a descending sort in "Sorting an array in descending order in Ruby", so spend some time looking through the selected answer to that question.