简体   繁体   English

红宝石哈希与块合并

[英]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, ... }`). 请注意,这适用于任意数量的哈希( bcd ,...)和任意数量的键({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] 由于gh都有键: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个经验丰富的红宝石:详细警报!

2 Hash#to_a could also be used here. 2 Hash#to_a也可以在这里使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM