首页 > 解决方案 > times 方法返回 nil 和 hash

问题描述

我所做的是从哈希中获取随机 2 个值并将它们与键匹配。我的代码有两个随机值,那部分效果很好。但是当涉及到 p hash[m] 时,我希望它会打印散列的键,而不是返回 nil。例如 :

我们的随机值是“cat”和“ocean”。该程序使用 times 方法自行选择此值。

所以当 m = "cat" hash[m] 应该是 "dog",但它是 nil。“海洋”也是如此。所以我有两个 nil 值。为什么?我怎样才能解决这个问题并获得正确的密钥?

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

标签: rubyrandomhashmap

解决方案


我的理解是给你一个哈希值,并希望随机选择两个值,然后识别与这些值关联的键。例如,如果

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样本没有替换(参见第 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}

请参阅哈希#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_atHash#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_values = values.uniq
  #=> [1, 2, 3]
random_values = unique_values.sample(2)
  #=> [3, 2]

是否使用valuesunique_values取决于应用程序的选择。无论如何,如果结果是[3,2],使用上面的第一种方法,我们得到

keys = hash.keys
  #=> [:a, :b, :c, :d]
associated_keys = random_values.map { |v| hash.key(v) }
  #=> [:c, :b] 

然而,这可能并不令人满意,因为它取决于hash's 键的顺序。认为:

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已经有 key v

(h[v] = h[v] || []) << k
  #=> (h[v] = h[v]) << k
  #=> h[v] << k

推荐阅读