【问题标题】:Rails mapping array of hashes onto single hashRails 将哈希数组映射到单个哈希
【发布时间】:2012-08-05 02:01:24
【问题描述】:

我有一个这样的哈希数组:

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

我正在尝试将其映射到像这样的单个哈希:

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

我已经使用了

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

但我想知道是否有可能以更惯用的方式执行此操作(最好不使用局部变量)。

我该怎么做?

【问题讨论】:

    标签: ruby-on-rails ruby arrays hash


    【解决方案1】:

    使用#inject

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

    【讨论】:

      【解决方案2】:

      怎么样:

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

      【讨论】:

      • 这个方案实际上与 Joshua 回答的相同,但在所有哈希上重复应用#merge(作为符号传递的方法名称)(将注入视为在项目之间注入运算符)。参考#inject。
      • 为什么我们不需要像 h.inject(&:merge) 这样的 & 符号?
      • 因为注入方法接受一个符号作为参数也被解释为方法名称。这是注入的功能。
      【解决方案3】:

      你可以编写Enumerable#reduce和Hash#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}
      

      【讨论】:

      • 谢谢,这是一个很好的答案 :) 解释得很好!
      • input.reduce(&:merge) 就足够了。
      • @redgetan 和 input.reduce(:merge) 有什么不同吗?
      • @David van Geest:在这种情况下,它们是等价的。此处使用的一元 & 符号从符号中构建了一个块。但是,reduce 有一个接受符号的特殊情况。我想避免使用一元 & 运算符来简化示例,但 redgetan 是正确的,在这种情况下初始值是可选的。
      • 请注意,如果您使用merge! 而不是merge,它将修改第一个哈希(您可能不想要),但不会为每个新合并创建中间哈希。
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        到目前为止,每个答案都建议使用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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-13
          • 2012-03-23
          • 2013-11-11
          • 2014-05-31
          相关资源
          最近更新 更多