ruby Rails将哈希数组Map到单个哈希

5cnsuln7  于 2023-08-04  发布在  Ruby
关注(0)|答案(6)|浏览(126)

我有一个哈希数组,如下所示:

[{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

字符串
我试着把它Map到一个散列上,像这样:

{"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}


我已经用

par={}
  mitem["params"].each { |h| h.each {|k,v| par[k]=v} }


但我想知道是否可以用更习惯的方式来实现这一点(最好不使用局部变量)。
我该怎么办?

6rqinv9w

6rqinv9w1#

您可以组合Enumerable#reduceHash#merge来实现您想要的功能。

input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
  is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

字符串
减少数组有点像在每个元素之间插入一个方法调用。
例如,[1, 2, 3].reduce(0, :+)就像说0 + 1 + 2 + 3,并给出6
在我们的例子中,我们做了类似的事情,但是使用了合并函数,它合并了两个哈希。

[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
  is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
  is {:a => 1, :b => 2, :c => 3}

yi0zb3m4

yi0zb3m42#

怎么样:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)

字符串

ktecyv1j

ktecyv1j3#

到目前为止,每个答案都建议使用Enumerable#reduce(或inject,这是一个别名)+ Hash#merge,但要注意,虽然干净,简洁和人类可读,但这个解决方案将非常耗时,并且在大型数组上占用大量内存。
我编制了不同的解决方案并对其进行了基准测试。

一些选项

a = [{'a' => {'x' => 1}}, {'b' => {'x' => 2}}]

# to_h
a.to_h { |h| [h.keys.first, h.values.first] }

# each_with_object
a.each_with_object({}) { |x, h| h.store(x.keys.first, x.values.first) }
# each_with_object (nested)
a.each_with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } }
# map.with_object
a.map.with_object({}) { |x, h| h.store(x.keys.first, x.values.first) }
# map.with_object (nested)
a.map.with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } }

# reduce + merge
a.reduce(:merge) # take wayyyyyy to much time on large arrays because Hash#merge creates a new hash on each iteration
# reduce + merge!
a.reduce(:merge!) # will modify a in an unexpected way

字符串

基准脚本

使用bmbm而不是bm很重要,以避免由于内存分配和垃圾收集的成本而导致的差异。

require 'benchmark'

a = (1..50_000).map { |x| { "a#{x}" => { 'x' => x } } }

Benchmark.bmbm do |x|
  x.report('to_h:') { a.to_h { |h| [h.keys.first, h.values.first] } }
  x.report('each_with_object:') { a.each_with_object({}) { |x, h| h.store(x.keys.first, x.values.first) } }
  x.report('each_with_object (nested):') { a.each_with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } } }
  x.report('map.with_object:') { a.map.with_object({}) { |x, h| h.store(x.keys.first, x.values.first) } }
  x.report('map.with_object (nested):') { a.map.with_object({}) { |x, h| x.each { |k, v| h.store(k, v) } } }
  x.report('reduce + merge:') { a.reduce(:merge) }
  x.report('reduce + merge!:') { a.reduce(:merge!) }
end


注意:我最初使用1_000_000项目数组进行了测试,但由于reduce + merge的花费是指数级的,所以它将花费很多时间来结束。

基准测试结果

50k项数组

Rehearsal --------------------------------------------------------------
to_h:                        0.031464   0.004003   0.035467 (  0.035644)
each_with_object:            0.018782   0.003025   0.021807 (  0.021978)
each_with_object (nested):   0.018848   0.000000   0.018848 (  0.018973)
map.with_object:             0.022634   0.000000   0.022634 (  0.022777)
map.with_object (nested):    0.020958   0.000222   0.021180 (  0.021325)
reduce + merge:              9.409533   0.222870   9.632403 (  9.713789)
reduce + merge!:             0.008547   0.000000   0.008547 (  0.008627)
----------------------------------------------------- total: 9.760886sec

                                 user     system      total        real
to_h:                        0.019744   0.000000   0.019744 (  0.019851)
each_with_object:            0.018324   0.000000   0.018324 (  0.018395)
each_with_object (nested):   0.029053   0.000000   0.029053 (  0.029251)
map.with_object:             0.021635   0.000000   0.021635 (  0.021782)
map.with_object (nested):    0.028842   0.000005   0.028847 (  0.029046)
reduce + merge:             17.331742   6.387505  23.719247 ( 23.925125)
reduce + merge!:             0.008255   0.000395   0.008650 (  0.008681)

2M项数组(不含reduce + merge

Rehearsal --------------------------------------------------------------
to_h:                        2.036005   0.062571   2.098576 (  2.116110)
each_with_object:            1.241308   0.023036   1.264344 (  1.273338)
each_with_object (nested):   1.126841   0.039636   1.166477 (  1.173382)
map.with_object:             2.208696   0.026286   2.234982 (  2.252559)
map.with_object (nested):    1.238949   0.023128   1.262077 (  1.270945)
reduce + merge!:             0.777382   0.013279   0.790661 (  0.797180)
----------------------------------------------------- total: 8.817117sec

                                 user     system      total        real
to_h:                        1.237030   0.000000   1.237030 (  1.247476)
each_with_object:            1.361288   0.016369   1.377657 (  1.388984)
each_with_object (nested):   1.765759   0.000000   1.765759 (  1.776274)
map.with_object:             1.439949   0.029580   1.469529 (  1.481832)
map.with_object (nested):    2.016688   0.019809   2.036497 (  2.051029)
reduce + merge!:             0.788528   0.000000   0.788528 (  0.794186)

kulphzqa

kulphzqa4#

使用#inject

hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

字符串

lokaqttq

lokaqttq5#

2023年更新:

Ruby 3中的merge方法现在支持多个参数。这意味着,我们可以提供多个哈希,它们将被合并在一起。

{a:1}.merge({b:2}, {c:3})
=> {:a=>1, :b=>2, :c=>3}

字符串
如果你有一个哈希数组,你可以使用splat运算符来分散参数:

hashes_to_merge = [{b:2}, {c:3}]
{a:1}.merge(*hashes_to_merge)
=> {:a=>1, :b=>2, :c=>3}

eoigrqb6

eoigrqb66#

在这里,您可以使用injectEnumerable类中的reduce,因为它们都是彼此的别名,因此没有性能优势。

sample = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

 result1 = sample.reduce(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

 result2 = sample.inject(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

字符串

相关问题