ruby哈希与块

时间:2017-07-27 16:50:59

标签: ruby hash

尝试使用ruby哈希合并!在多个哈希上,以空哈希

开头
a = {}
b = {x: 1.2, y: 1.3}
c = {x: 1.4, y: 1.5}
fact = 100 # need to multiply values that are merged in with this
a.merge!(b) {|k,v1,v2| v1 + v2 * fact} # it doesn't multiply values with     fact
a.merge!(c) {|k,v1,v2| v1 + v2 * fact} #it does multiply values with fact

所以第一次合并并没有给出我期待的结果,而第二次合并确实如此。请注意,在真正的应用程序键中,不限于x和y,可以有许多不同的键。

2 个答案:

答案 0 :(得分:1)

第一次合并的工作原理如documentation

所述

当两个哈希中都存在一个键时,仅调用该块来解决冲突。在第一次调用Hash#merge!时,a为空,因此不会发生冲突,b的内容会被复制到a而不做任何更改。

您可以通过a初始化{x: 0, y: 0}来修复代码。

答案 1 :(得分:1)

我倾向于按如下方式执行合并。

a = {}
b = {x: 1.2, y: 1.3}
c = {x: 1.4, y: 1.5}
[b, c].each_with_object(a) { |g,h| h.update(g) { |_,o,n| o+n } }.
  tap { |h| h.keys.each { |k| h[k] *= 10 } }
  #=> {:x=>25.999999999999996, :y=>28.0}

请注意,这适用于任意数量的哈希值(bcd,...)和任意数量的键({x:1.2,y:1.3, z:2.1,...}`)。

步骤如下 1

e = [b, c].each_with_object(a)
  #=> #<Enumerator: [{:x=>1.2, :y=>1.3}, {:x=>1.4, :y=>1.5}]:each_with_object({})>

我们可以通过应用Enumerable#entries 2 来查看此枚举器将生成​​的值:

e.entries
  #=> [[{:x=>1.2, :y=>1.3}, {}], [{:x=>1.4, :y=>1.5}, {}]]

我们可以使用Enumerator#next生成e的第一个值,并为其分配两个块变量(即&#34;将e.next传递给块&#34; ):

g,h = e.next
  #=> [{:x=>1.2, :y=>1.3}, {}]
g #=> {:x=>1.2, :y=>1.3}
h #=> {}

接下来我们执行块计算。

f = h.update(g) { |_,o,n| o+n }
  #=> {:x=>1.2, :y=>1.3}

这里我使用了Hash.update(aka merge!)的形式,它使用一个块来确定合并的两个哈希中存在的键的值。 (有关详细信息,请参阅文档。)由于h现在为空(无密钥),因此该块不用于此合并。

现在生成e的下一个和最后一个值,并重复该过程。

g,h = e.next
  #=> [{:x=>1.4, :y=>1.5}, {:x=>1.2, :y=>1.3}]
g #=> {:x=>1.4, :y=>1.5}
h #=> {:x=>1.2, :y=>1.3}
f = h.update(g) { |_,o,n| o+n }
  #=> {:x=>2.5999999999999996, :y=>2.8}

由于gh都有一个键:x,因此该块用于确定h[:x]的新值

_ #=> :x
o #=> 1.4
n #=> 1.2
h[:x] = o + n
  #=> 2.6

同样,h[:y| = 2.8

最后一步使用Object#tap10将每个值复用。

f.tap { |g| g.keys.each { |k| h[k] *= 10 } }
  #=> {:x=>25.999999999999996, :y=>28.0}

tap除了保存一行代码和创建局部变量之外别无其他,正如我可以写的那样:

h = [b, c].each_with_object(a) { |g,h| h.update(g) { |_,o,n| o+n } }
h.keys.each { |k| h[k] *= 10 }
h

另一个选项(不使用tap)是写:

f = [b, c].flat_map(&:keys).uniq.product([0]).to_h
  #=> {:x=>0, :y=>0}
[b, c].each_with_object(f) { |g,h| h.update(g) { |_,o,n| o+10*n } }
  #=> {:x=>26.0, :y=>28.0}

1经验丰富的Rubiests:GORY DETAIL ALERT!

2 Hash#to_a也可以在这里使用。