如果它们的第一个值相同,如何有条件地计算多维数组中的平均值



我有一个包含值和数组的数组,如下所示:

arr = [ 
        [ 0, [ [22,3],[23,5] ] ],
        [ 0, [ [22,1],[23,2] ] ],
        [ 1, [ [22,4],[23,4] ] ],
        [ 1, [ [22,2],[23,4] ] ]
      ]

我想根据前两个元素计算平均值,并希望在哈希或数组中有一个结果集,如下所示:

result = {
          22 => [(3+1)/2, (4+2)/2], 
          23 => [(5+2)/2, (4+4)/2] 
         }

其中例如:

key 为 22,value 是一个数组,包含输入数组中第三个元素的平均值,按第一个元素 3 和 1、4 和 2 分组,并按第一个元素 0 和 1 排序


如何创建数组

也许提及我的逻辑可能会有所帮助。

数组是通过以下代码从我的 ActiveRecord 对象中获取的:

arr = u.feedbacks.map{|f| [f.week, 
                           f.answers.map{|a|  [a.question.id, a.name.to_i]}]}

其中模型关联如下:

feedback belongs_to :user
feedback has_and_belongs_to_many :answers
answer belongs_to :question

对于每个问题,我想创建一个数组,其中包含按反馈周分组的平均答案。

通过一些调试,以下内容应该有助于更快地获得结果:

Answer.
joins(:question, :feedbacks). # assuming that answer has_many feedbacks
group(["questions.id", "feedbacks.week"]). # assuming week is integer column
average("CAST(answers.name AS INT)"). # assuming that name is string-y column
each_with_object({}) do |(keys, average), hash|
  question_id, week = keys
  hash[question_id] ||= []
  hash[question_id][week] = average
end

如果你想保持现状(不建议),那么一个有效的(尽管很难遵循)的解决方案是这样的:

arr = [ 
  [0, [[22, 3], [23, 5]]],
  [0, [[22, 1], [23, 2]]],
  [1, [[22, 4], [23, 4]]],
  [1, [[22, 2], [23, 4]]]
]
arr.each_with_object({}) do |(a, b), hash|
  c, d, e, f = b.flatten
  # for first row this will be c, d, e, f = 22, 3, 23, 5
  hash[c] ||= []
  hash[c][a] ||= []
  hash[c][a] << d
  hash[e] ||= []
  hash[e][a] ||= []
  hash[e][a] << f

end.each_with_object({}) do |(k, v), hash|
  # k are your 'keys' like 22, 23
  # v is an array of arrays that you want to find out the averages of
  hash[k] = 
    v.map do |array|
      array.reduce(:+).fdiv(array.size)
    end
end

如果是我,我可以按照我的方式,我会重构arr从一开始就创建的方式,因为

  1. 数组的维度是违反直觉的
  2. 转换再次造成损失,进而影响可读性,进而影响可维护性。

但是我没有比从您显示的代码中看到的更多的见解。所以,我玩了一下,也许下面的代码是你想要的?

totals = {}
arr.each do |row|
    index, answers = row
    answers.each do |answer|
        question, count = answer
        totals[question] ||= []
        totals[question][index] ||= []
        totals[question][index] << count
    end
end

下面是totals的输出,到那时,获得平均值是微不足道的。

{
    22 =>[[3, 1], [4, 2]], 
    23=>[[5, 2], [4, 4]]
}

编辑 以下是我使用从@Humza中学到each_with_object制定的解决方案

arr = [ 
    [ 0, [ [22,3],[23,5] ] ],
    [ 0, [ [22,1],[23,2] ] ],
    [ 1, [ [22,4],[23,4] ] ],
    [ 1, [ [22,2],[23,4] ] ]
]
result = arr.each_with_object({}) do |(index, feedbacks), totals|
    feedbacks.each do |(question, count)|
        totals[question] ||= {}
        totals[question][index] ||= []
        totals[question][index] << count
    end
    totals
end.each_with_object({}) do |(question, totals), result|
    result[question] = totals.map do |(index, total)|
        total.reduce(:+).fdiv(total.length)
    end
end
puts result.inspect
## Output 
# {22=>[2.0, 3.0], 23=>[3.5, 4.0]}

最新更新