Reputation: 147
What I did is get random 2 values from hash and match them with the keys. My code gets two random values, that part works well. But when it comes to p hash[m], I expected it would print the key of hash, instead it returns nil. For example :
Our random values are "cat" and "ocean". The program pick this values by itself with times method.
So when m = "cat" hash[m] should be "dog", but it's nil. The same thing goes for "ocean" too. So I have two nil values. Why? How can I fix that and get the right key?
hash = { "foo" => "bar", "cat" => "dog", "ear" => "nose", "ocean" => "sea"}
value = hash.values
2.times do
m = value[rand(value.size)]
p hash[m] #Erroneous part
end
Upvotes: 1
Views: 141
Reputation: 110685
My understanding is that you are given a hash and wish to select two values at random and then identify the keys associated with those values. For example, if
hash = { :a=>1, :b=>2, :c=>3 }
the values are
values = hash.values
#=> [1, 2, 3]
In this example the values are unique. Later I will address the general case where the values are not unique (e.g., { :a=>1, :b=>2, :c=>3, :d=>2 }
).
If two values are to be selected randomly we would write
random_values = values.sample(2)
#=> [2, 1]
In statistical terms, Array#sample samples without replacement (see paragraph 3). By contrast,
random_values = 2.times.map { values[rand(values.size)] }
samples two values with replacement, so random_values
might equal, for example, [2, 2]
.
Once random_values
is obtained (assume [2, 1]
) there are two ways to obtain the associated keys:
The first is as follows.
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:b, :a]
though it might be more useful to construct a hash that associates keys with the random values:
random_values.each_with_object({}) { |v,h| h[v] = hash.key(v) }
#=> {2=>:b, 1=>:a}
See Hash#key. The second way is to use Hash#invert:
hsaH = hash.invert
#=> { 1=>:a, 2=>:b, 3=>:c }
Then we simply compute:
hsaH.values_at(*random_values)
#=> [:b, :a]
or
hsaH.slice(*random_values)
#=> {2=>:b, 1=>:a}
See Hash#values_at and Hash#slice.
Now consider the general case where the values are not unique. Suppose
hash = { :a=>1, :b=>2, :c=>3, :d=>2 }
where keys :b
and :d
have the same value.
values = hash.values
#=> [1, 2, 3, 2]
Then if we write
random_value = values.sample(2)
we could obtain random_values #=> [2, 2]
. That may be permitted, but if two unique values are needed we would write
unique_values = values.uniq
#=> [1, 2, 3]
random_values = unique_values.sample(2)
#=> [3, 2]
The choice of whether to use values
or unique_values
depends on the application. Regardless, if the result is [3,2]
, using the first approach above we obtain
keys = hash.keys
#=> [:a, :b, :c, :d]
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:c, :b]
This may be unsatisfactory, however, as it depends on the ordering of the hash
's keys. Suppose:
hash = { :a=>1, :d=>2, :c=>3, :b=>2 }
In this case we get a different answer:
keys = hash.keys
#=> [:a, :d, :c, :b]
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:c, :d]
Simlarly,
{ :a=>1, :b=>2, :c=>3, :d=>2 }.invert
#=> {1=>:a, 2=>:d, 3=>:c}
{ :a=>1, :d=>2, :c=>3, :b=>2 }.invert
#=> {1=>:a, 2=>:b, 3=>:c}
the values of 2
being different.
For the general case (again, depending on the application), we might consider writing the following.
hash = { :a=>1, :d=>2, :c=>3, :b=>2 }
values_to_keys = hash.each_with_object({}) do |(k,v),h|
(h[v] ||= []) << k
end
#=> {1=>[:a], 2=>[:d, :b], 3=>[:c]}
Then
values = hash.values
random_values = values.sample(2)
#=> [3, 1]
random_values.map { |v| [v, values_to_keys[v].shift] }
#=> [[3, :c], [1, :a]]
A second example:
random_values = values.sample(2)
#=> [2, 2]
random_values.map { |v| [v, values_to_keys[v].shift] }
#=> [[2, :d], [2, :b]]
Note that in the calculation of values_to_keys
, (h[v] ||= []) << k
expands to
(h[v] = h[v] || []) << k
so if h
does not have a key v
, this becomes
(h[v] = nil || []) << k
(h[v] = []) << k
causing h[v]
to be set equal to an empty array, which is returned, followed by k
being appended to that empty array. By contrast, if h
already has a key v
,
(h[v] = h[v] || []) << k
#=> (h[v] = h[v]) << k
#=> h[v] << k
Upvotes: 2
Reputation: 12347
Select the key/value pairs at random by selecting the keys at random, since that's easier:
my_hash = { "foo" => "bar", "cat" => "dog", "ear" => "nose", "ocean" => "sea"}
my_keys = my_hash.keys
2.times do
rand_key = my_keys[rand(my_keys.size)]
rand_val = my_hash[rand_key]
puts "#{rand_key} => #{rand_val}"
end
Upvotes: 1