假设
original_hash = {:a=>1, :b=>2, :c=>2, :d=>2, :e=>3, :f=>4, :g=>4}
如果您只对返回包含唯一值的哈希 uniques_hash 感兴趣,您可以编写以下代码。
uniques_hash = original_hash.invert.invert
#=> {:a=>1, :d=>2, :e=>3, :g=>4}
中间步骤是
original_hash.invert
#=> {1=>:a, 2=>:d, 3=>:e, 4=>:g}
见Hash#invert。请注意,定义的uniques_hash 本身并不是唯一的。它可以是以下任何一种。
{:a=>1, :b=>2, :e=>3, :f=>4}
{:a=>1, :b=>2, :e=>3, :g=>4}
{:a=>1, :c=>2, :e=>3, :f=>4}
{:a=>1, :c=>2, :e=>3, :g=>4}
{:a=>1, :d=>2, :e=>3, :f=>4}
{:a=>1, :d=>2, :e=>3, :g=>4}
另一种方法是使用Enumerable#uniq 和Array#to_h。
unique_hash = original_hash.uniq(&:last).to_h
#=> {:a=>1, :b=>2, :e=>3, :f=>4}
中间计算是
original_hash.uniq(&:last)
#=> [[:a, 1], [:b, 2], [:e, 3], [:f, 4]]
这是的简写
original_hash.uniq { |_k,v| v }
推测duplicates_hash 的每个键都是original_hash 中的一个值,而该键的值是original_hash 中k 中original_hash[k] == v 中的那些键的数组。
计算duplicates_hash的一种方法如下。
duplicates_hash = original_hash.each_with_object({}) do |(k,v),h|
h[v] = (h[v] || []) << k
end
#=> {1=>[:a], 2=>[:b, :c, :d], 3=>[:e], 4=>[:f, :g]}
这个也可以写
duplicates_hash = original_hash.
each_with_object(Hash.new { |h,k| h[k] = [] }) { |(k,v),h| h[v] << k }
#=> {1=>[:a], 2=>[:b, :c, :d], 3=>[:e], 4=>[:f, :g]}
见Hash::new。两种形式都等价于
duplicates_hash = original_hash.each_with_object({}) do |(k,v),h|
h[v] = [] unless h.key?(v)
h[v] << k
end
将块变量写为|(k,v),h| 使用array decomposition。
我们有一个枚举器,它将生成值并将它们传递给它的块。
enum = original_hash.each_with_object({})
#=> #<Enumerator: {:a=>1, :b=>2, :c=>2, :d=>2, :e=>3, :f=>4, :g=>4}:
# each_with_object({})>
枚举器是 Enumerator 类的实例。
生成枚举器的第一个值,并为块变量赋值,如下所示:
(k,v),h = enum.next
#=> [[:a, 1], {}]
可以看到数组分解将这个包含两个元素的数组拆分如下:
k #=> :a
v #=> 1
h #=> {}
注意左边的括号如何对应右边的内括号。然后使用这些变量执行块计算。
h[v] = (h[v] || []) << k
#=> [:a]
现在,
h #=> {1=>[:a]}
然后由枚举器生成下一个值并进行块计算。
(k,v),h = enum.next
#=> [[:b, 2], {1=>[:a]}]
k #=> :b
v #=> 2
h #=> {1=>[:a]}
h[v] = (h[v] || []) << k
现在
h #=> {1=>[:a], 2=>[:b]}
这样一直持续到
enum.next
#=> Stop Interation (exception)
导致 Ruby 返回 h 的值。
请注意,通过首先计算duplicates_hash,我们可以计算uniques_hash,如下所示。
keeper_keys = duplicates_hash.values.map(&:first)
#=> [:a, :b, :e, :f]
unique_keys = original_hash.slice(*keeper_keys)
#=> {:a=>1, :b=>2, :e=>3, :f=>4}
或
unique_keys = original_hash.slice(*duplicates_hash.values.map(&:first))
#=> {:a=>1, :b=>2, :e=>3, :f=>4}
见Hash#slice。如果某人因偏爱某些键而感到内疚,则可以改写
unique_keys = original_hash.slice(*duplicates_hash.values.map(&:sample))
#=> {:a=>1, :b=>2, :e=>3, :g=>4}
见Array#sample。