【问题标题】:Merge array of hashes: value should be the average of merged values合并哈希数组:值应该是合并值的平均值
【发布时间】:2018-05-22 02:00:32
【问题描述】:

问题:将具有相同值的哈希数组与指定键的值合并,并找到其他键的平均值。

我的解决方案似乎很难看

数据:

require 'pp'

arr = [{:red=>346.0,
  :unu=>10.0,
  :used=>20147.0,
  :acc_id=>550,
  :percent=>0.01},
 {:red=>0.0,
  :unu=>1.0,
  :used=>66.0,
  :acc_id=>550,
  :percent=>0.06},
 {:red=>120.0,
  :unu=>11.0,
  :used=>166.0,
  :acc_id=>550,
  :percent=>10.06},
 {:red=>1306.0,
  :unu=>1.0,
  :used=>13259.0,
  :acc_id=>9999,
  :percent=>0.0}]

在当前示例中,我们应该将 3 个哈希与 (:acc_id = 550) 合并,结果数组应包含两个哈希(合并哈希与 :acc_id = 550 和未触及哈希与 :acc_id = 9999)

算法:

data = []
arr.group_by{|h| h[:acc_id] }.map {|_, arr_of_hashes|
  sz = arr_of_hashes.size
  if sz > 1
    arr_of_hashes = arr_of_hashes.inject{|memo, el|
      memo.merge(el) {|k, old_v, new_v| old_v + new_v}
    }

    arr_of_hashes.map {|k, v| arr_of_hashes[k] = v / sz}
  end
  data << arr_of_hashes if arr_of_hashes.is_a? Hash
  data << arr_of_hashes[0] if arr_of_hashes.is_a? Array
}

pp data

预期结果: 合并哈希数组

[{:red=>155.33333333333334,
  :unu=>7.333333333333333,
  :used=>6793.0,
  :acc_id=>550,
  :percent=>3.376666666666667},
 {:red=>1306.0,
  :unu=>1.0,
  :used=>13259.0,
  :acc_id=>9999,
  :percent=>0.0}]

... ... ...

【问题讨论】:

  • 当你给出一个例子时,你应该总是展示你想要的结果。
  • 我假设你想要每个键的平均值,取自 arr 中的所有哈希值。对吗?
  • @CarySwoveland 添加了预期结果。我想合并具有相同键值的哈希(在当前示例中为 :acc_id )并找到所有其他键的平均值
  • 对于您的示例,“指定密钥”是什么?
  • @CarySwoveland :acc_id。还在描述中添加了一些解释

标签: ruby algorithm


【解决方案1】:

我发现你在acc_id 上执行/+ 的一个错误已经重构了代码 让我们试试这个,但我想我们仍然会改进它。

data = []
arr.group_by{|h| h[:acc_id] }.map {|_, arr_of_hashes|
  sz = arr_of_hashes.size
  result = Hash.new(0)
  arr_of_hashes.map{ |hash| hash.map{ |k,v| result[k] += v/sz unless k == :acc_id } }
  result[:acc_id] = arr_of_hashes.first[:acc_id]
  data << result
}

【讨论】:

    【解决方案2】:

    我建议您按如下方式计算平均值。我假设,与示例一样,所有哈希都具有相同的键(尽管它们不一定以相同的顺序出现)。

    代码

    def doit(arr, key)
      keys = arr.first.keys
      arr.group_by { |g| g[key] }.
          map do |_,a| 
            averages = a.map { |h| h.values_at(*keys) }.
                         transpose.
                         map { |v| v.sum.fdiv(v.size) }
            keys.zip(averages).to_h
          end
    end 
    

    示例

    请注意,我的哈希数组与问题示例中给出的哈希数组有些不同。具体来说,有三组(而不是两组)哈希,其中键 :acc_id 的值具有共同值。

    arr = [{ red: 346.0,  unu: 10.0, used: 20147.0, acc_id: 550,  percent: 0.01 },
           { red: 0.0,    unu: 1.0,  used: 66.0,    acc_id: 550,  percent: 0.06 },
           { red: 120.0,  unu: 11.0, used: 166.0,   acc_id: 10,   percent: 10.06 },
           { red: 100.0,  unu: 19.0, used: 170.0,   acc_id: 10,   percent: 11.56 },
           { red: 1306.0, unu: 1.0,  used: 13259.0, acc_id: 9999, percent: 0.0 }]
    
    doit(arr, :acc_id)
      #=> [{:red=>173.0, :unu=>5.5, :used=>10106.5, :acc_id=>550.0, :percent=>0.035},
      #    {:red=>110.0, :unu=>15.0, :used=>168.0, :acc_id=>10.0, :percent=>10.81},
      #    {:red=>1306.0, :unu=>1.0, :used=>13259.0, :acc_id=>9999.0, :percent=>0.0}]
    

    说明

    参见Enumerable#group_byArray#sum(后者在v2.4 中首次亮相)。

    步骤如下。

    key = :acc_id
    keys = arr.first.keys
      #=> [:red, :unu, :used, :acc_id, :percent]
    b = arr.group_by { |g| g[key] }
      #=> { 550=>[{:red=>346.0, :unu=>10.0, :used=>20147.0, :acc_id=>550, :percent=>0.01},
      #           {:red=>0.0, :unu=>1.0, :used=>66.0, :acc_id=>550, :percent=>0.06}],
      #      10=>[{:red=>120.0, :unu=>11.0, :used=>166.0, :acc_id=>10, :percent=>10.06},
      #           {:red=>100.0, :unu=>19.0, :used=>170.0, :acc_id=>10, :percent=>11.56}],
      #    9999=>[{:red=>1306.0, :unu=>1.0, :used=>13259.0, :acc_id=>9999, :percent=>0.0}]}
    b.map do |_,a| 
      averages = a.map { |h| h.values_at(*keys) }.
                   transpose.
                   map { |v| v.sum.fdiv(v.size) }
      keys.zip(averages).to_h
    end 
      #=> <the array of hashes shown above>
    

    【讨论】:

    • 看起来像 rails 解决方案,因为 'sum' 方法。但是我用 inject(0, :+) 替换了它,它不像预期的那样工作
    • 看起来不错的解决方案,但它比原始解决方案慢 3 倍。包含措施的文件:link
    • 在使用示例哈希数组进行测试时,您发现“它慢了 x3 倍”。其他数据可能更快或更慢。另请注意,我的代码在两个方面与您的不同: 1. 我不假设任何关于哈希键的排序(通过使用Hash#values_at)。即使(对于 Ruby v1.9+)键在所有哈希中具有相同的顺序,您的代码在使用早期 Ruby 版本运行时可能会产生错误的结果,其中没有键顺序的概念。 2. 计算出平均值后,我会构造一个散列来将它们与键关联起来 (keys.zip(averages).to_h)。
    猜你喜欢
    • 1970-01-01
    • 2019-03-03
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-29
    • 1970-01-01
    相关资源
    最近更新 更多