尝试使用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,可以有许多不同的键。
答案 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}
请注意,这适用于任意数量的哈希值(b
,c
,d
,...)和任意数量的键({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}
由于g
和h
都有一个键:x
,因此该块用于确定h[:x]
的新值
_ #=> :x
o #=> 1.4
n #=> 1.2
h[:x] = o + n
#=> 2.6
同样,h[:y| = 2.8
。
最后一步使用Object#tap按10
将每个值复用。
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也可以在这里使用。