[英]ruby hash merge with a block
Trying to use ruby Hash merge! 尝试使用ruby哈希合并! on multiple hashes, starting with an empty hash
在多个散列上,从一个空散列开始
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
So first merge does not give me result I was expecting, while the second merge does. 因此,第一次合并并没有给我期望的结果,而第二次合并却有。 Please note that in real app keys are not limited to x and y, there can be many different keys.
请注意,在实际应用中,键不仅限于x和y,可以有许多不同的键。
The first merge works as described in the documentation . 第一次合并的工作方式如文档中所述。
The block is invoked only to solve conflicts, when a key is present in both hashes. 当两个哈希中都存在密钥时,仅调用该块以解决冲突。 On the first call to
Hash#merge!
在第一次调用
Hash#merge!
, a
is empty, hence no conflict occurred and the content of b
is copied into a
without any changes. ,
a
为空,因此不发生冲突,并且b
的内容被复制到a
而没有任何更改。
You can fix the code by initializing a
with {x: 0, y: 0}
. 您可以通过使用
{x: 0, y: 0}
初始化a
来修复代码。
I would be inclined to perform the merge as follows. 我倾向于按如下所示执行合并。
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}
Note that this works with any number of hashes ( b
, c
, d
, ...) and any number of keys ({ x: 1.2, y: 1.3, z: 2.1, ... }`). 请注意,这适用于任意数量的哈希(
b
, c
, d
,...)和任意数量的键({x:1.2,y:1.3,z:2.1,...}`)。
The steps are as follows 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({})>
We can see the values that will be generated by this enumerator by applying Enumerable#entries 2 : 我们可以通过应用Enumerable#entries 2看到此枚举器将生成的值:
e.entries
#=> [[{:x=>1.2, :y=>1.3}, {}], [{:x=>1.4, :y=>1.5}, {}]]
We can use Enumerator#next to generate the first value of e
and assign the two block variables to it (that is, "pass e.next
to the block"): 我们可以使用Enumerator#next生成
e
的第一个值,并为其分配两个块变量(即,“将e.next
传递给该块”):
g,h = e.next
#=> [{:x=>1.2, :y=>1.3}, {}]
g #=> {:x=>1.2, :y=>1.3}
h #=> {}
Next we perform the block calculation. 接下来,我们执行块计算。
f = h.update(g) { |_,o,n| o+n }
#=> {:x=>1.2, :y=>1.3}
Here I have used the form of Hash.update (aka merge!
) which employs a block to determine the values of keys that are present in both hashes being merged. 在这里,我使用了Hash.update的形式(又名
merge!
),它使用一个块来确定要合并的两个哈希中存在的键的值。 (See the doc for details.) As h
is now empty (no keys), the block is not used for this merge. (有关详细信息,请参阅文档。)由于
h
现在为空(无键),因此该块不用于合并。
The next and last value of e
is now generated and the process is repeated. 现在生成
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}
Since g
and h
both have a key :x
, the block is used to determine the new value of h[:x]
由于
g
和h
都有键:x
,所以该块用于确定h[:x]
的新值
_ #=> :x
o #=> 1.4
n #=> 1.2
h[:x] = o + n
#=> 2.6
Similarly, h[:y| = 2.8
同样,
h[:y| = 2.8
h[:y| = 2.8
. h[:y| = 2.8
。
The last step uses Object#tap to multiple each value by 10
. 最后一步使用Object#tap将每个值乘以
10
。
f.tap { |g| g.keys.each { |k| h[k] *= 10 } }
#=> {:x=>25.999999999999996, :y=>28.0}
tap
does nothing more than save a line of code and the creation of a local variable, as I could have instead written: 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
Another option (that does not use tap
) is to write: 另一个选项(不使用
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 Experienced Rubiests: GORY DETAIL ALERT! 1个经验丰富的红宝石:详细警报!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.