我的理解是给你一个哈希值,并希望随机选择两个值,然后识别与这些值关联的键。例如,如果
hash = { :a=>1, :b=>2, :c=>3 }
值是
values = hash.values
#=> [1, 2, 3]
在此示例中,值是唯一的。稍后我将讨论值不唯一的一般情况(例如,{ :a=>1, :b=>2, :c=>3, :d=>2 })。
如果要随机选择两个值,我们会写
random_values = values.sample(2)
#=> [2, 1]
在统计方面,Array#sample 抽样 without replacement(见第 3 段)。相比之下,
random_values = 2.times.map { values[rand(values.size)] }
对两个值进行抽样替换,因此random_values 可能等于[2, 2]。
一旦获得random_values(假设[2, 1]),有两种方式获得关联的密钥:
第一个如下。
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:b, :a]
虽然构造一个将键与随机值相关联的哈希可能更有用:
random_values.each_with_object({}) { |v,h| h[v] = hash.key(v) }
#=> {2=>:b, 1=>:a}
见Hash#key。第二种方式是使用Hash#invert:
hsaH = hash.invert
#=> { 1=>:a, 2=>:b, 3=>:c }
然后我们简单地计算:
hsaH.values_at(*random_values)
#=> [:b, :a]
或
hsaH.slice(*random_values)
#=> {2=>:b, 1=>:a}
请参阅 Hash#values_at 和 Hash#slice。
现在考虑值不唯一的一般情况。假设
hash = { :a=>1, :b=>2, :c=>3, :d=>2 }
其中键 :b 和 :d 具有相同的值。
values = hash.values
#=> [1, 2, 3, 2]
如果我们写
random_value = values.sample(2)
我们可以获得random_values #=> [2, 2]。这可能是允许的,但如果需要两个 unique 值,我们将编写
unique_values = values.uniq
#=> [1, 2, 3]
random_values = unique_values.sample(2)
#=> [3, 2]
选择使用values 还是unique_values 取决于应用程序。无论如何,如果结果是[3,2],使用上面的第一种方法我们得到
keys = hash.keys
#=> [:a, :b, :c, :d]
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:c, :b]
然而,这可能并不令人满意,因为它取决于hash 的键的顺序。假设:
hash = { :a=>1, :d=>2, :c=>3, :b=>2 }
在这种情况下,我们得到了不同的答案:
keys = hash.keys
#=> [:a, :d, :c, :b]
associated_keys = random_values.map { |v| hash.key(v) }
#=> [:c, :d]
同样,
{ :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}
2 的值不同。
对于一般情况(同样,取决于应用程序),我们可以考虑编写以下内容。
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]}
然后
values = hash.values
random_values = values.sample(2)
#=> [3, 1]
random_values.map { |v| [v, values_to_keys[v].shift] }
#=> [[3, :c], [1, :a]]
第二个例子:
random_values = values.sample(2)
#=> [2, 2]
random_values.map { |v| [v, values_to_keys[v].shift] }
#=> [[2, :d], [2, :b]]
注意在values_to_keys的计算中,(h[v] ||= []) << k展开为
(h[v] = h[v] || []) << k
所以如果h没有密钥v,这就变成了
(h[v] = nil || []) << k
(h[v] = []) << k
导致h[v] 被设置为等于一个空数组,然后返回该数组,然后将k 附加到该空数组。相比之下,如果h 已经有一个密钥v,
(h[v] = h[v] || []) << k
#=> (h[v] = h[v]) << k
#=> h[v] << k