如何对具有相同结构的数组数组中的值进行分组和求和

时间:2017-07-21 13:58:23

标签: ruby-on-rails ruby

我在数据属性中有这个数组数组,每个活动对应一个小时的课堂出勤率。

我需要按小时计算每项活动的总出勤率。 有可能对它进行分组和总结吗?

实施例: 小时7的正确结果是50 + 60 = 110

[{:name=>"cardio", 
:data=>[["06", 999], ["07", 50], ["08", 0], ["09", 154], ["10", 1059], ["11", 90], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}, 
:name=>"swimming", 
:data=>[["06", 0], ["07", 60], ["08", 0], ["09", 0], ["10", 90], ["11", 50], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]

预期结果:

:data=>[["06", 999], ["07", 110], ["08", 0], ["09", 154], ["10", 1149], ["11", 140], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]

4 个答案:

答案 0 :(得分:3)

案例1::data的值是相同大小的数组,其元素(双元素数组)按其第一个元素排序相同

arr = [{ :name=>"cardio",   :data=>[["06", 999], ["07", 50], ["08", 0]] }, 
       { :name=>"swimming", :data=>[["06",   0], ["07", 60], ["08", 0]] }]

a, b = arr.map { |h| h[:data].transpose }.transpose
  #=> [[["06", "07", "08"], ["06", "07", "08"]], [[999, 50, 0], [0, 60, 0]]]
{ :data=>a.first.zip(b.transpose.map { |col| col.reduce(:+) }) }
  #=> {:data=>[["06", 999], ["07", 110], ["08", 0]]}

案例2::data的值是可能大小不同的数组,其元素(双元素数组)的第一个元素可能不是相同的

arr = [{ :name=>"cardio",   :data=>[["05", 999], ["07", 50], ["08",  0]] }, 
       { :name=>"swimming", :data=>[["08", 300], ["04", 33], ["07", 60]] }] 

{ :data=>arr.flat_map { |g| g[:data] }.
    each_with_object(Hash.new(0)) { |(f,v),h| h[f] += v }.
    sort.
    to_a }
  #=>  {:data=>[["04", 33], ["05", 999], ["07", 110], ["08", 300]]}

注意:

  • 根据要求,可能不需要sort
  • 无论:data的值是否并行定义
  • ,都可以使用第二种方法
  • 第二种方法使用Hash::new的形式,它接受一个参数(默认值),此处为零。这有时称为计算哈希。有关详细信息,请参阅文档。

答案 1 :(得分:1)

  

如何对具有相同结构的数组数组中的值进行分组和求和

你应该这样做:

[{:name=>"cardio", 
:data=>[["06", 999], ["07", 50], ["08", 0], ["09", 154], ["10", 1059], ["11", 90], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}, 
:name=>"swimming", 
:data=>[["06", 0], ["07", 60], ["08", 0], ["09", 0], ["10", 90], ["11", 50], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]

试试这个:

act.map{|h| h[:data]}.flatten(1).group_by(&:first).map { |k,v| [k, v.map(&:last).inject(:+)] }
# => [["06", 999], ["07", 110], ["08", 0], ["09", 154], ["10", 1149], ["11", 140], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]] 

希望这会对你有所帮助

答案 2 :(得分:1)

假设:

act=[{:name=>"cardio", 
:data=>[["06", 999], ["07", 50], ["08", 0], ["09", 154], ["10", 1059], ["11", 90], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}, 
{:name=>"swimming", 
:data=>[["06", 0], ["07", 60], ["08", 0], ["09", 0], ["10", 90], ["11", 50], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]

你可以这样做:

> act.map{ |h| h[:data] }
       .flatten(1)
       .group_by{ |h,n| h }
       .map { |k,v| [k, v.map(&:last).sum] }
=> [["06", 999], ["07", 110], ["08", 0], ["09", 154], ["10", 1149], ["11", 140], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]

或者,

> act.map{|h| h[:data]}
     .flatten(1)
     .each_with_object(Hash.new(0)) {|e,h| h[e[0]]+=e[1]}.to_a

也有效。

答案 3 :(得分:0)

actions=[{:name=>"cardio", 
:data=>[["06", 999], ["07", 50], ["08", 0], ["09", 154], ["10", 1059], ["11", 90], ["12", 30], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}, 
{:name=>"swimming", 
:data=>[["06", 0], ["07", 60], ["08", 0], ["09", 0], ["10", 90], ["11", 50], ["12", 0], ["13", 0], ["14", 0], ["15", 0], ["16", 0], ["17", 0], ["18", 0], ["19", 0], ["20", 0], ["21", 0], ["22", 0], ["23", 0]]}]

actions.map{|act| act[:data]}.reduce({}) do |acc, data|
  acc.merge(data.to_h) {|k, v1, v2| v1 + v2}
end.to_a

此解决方案利用了Ruby' Hash保持插入顺序的事实。

该算法的优点在于它允许在任一数据阵列中丢失密钥。