Как сгруппировать и добавить значения из вложенных хешей и массивов с одинаковым ключом? - PullRequest
0 голосов
/ 02 февраля 2019

Я пытаюсь получить сумму баллов и среднюю оценку для каждого учащегося в этой комбинации хэшей и массивов, но все мои попытки возвращают только общую сумму для всех записей.Есть идеи?

student_data = 
  {"ST4"=>[{:student_id=>"ST4", :points=> 5, :grade=>5}, 
           {:student_id=>"ST4", :points=>10, :grade=>4}, 
           {:student_id=>"ST4", :points=>20, :grade=>5}], 
   "ST1"=>[{:student_id=>"ST1", :points=>10, :grade=>3}, 
           {:student_id=>"ST1", :points=>30, :grade=>4}, 
           {:student_id=>"ST1", :points=>45, :grade=>2}], 
   "ST2"=>[{:student_id=>"ST2", :points=>25, :grade=>5}, 
           {:student_id=>"ST2", :points=>15, :grade=>1}, 
           {:student_id=>"ST2", :points=>35, :grade=>3}], 
   "ST3"=>[{:student_id=>"ST3", :points=> 5, :grade=>5}, 
           {:student_id=>"ST3", :points=>50, :grade=>2}]}

Ответы [ 3 ]

0 голосов
/ 02 февраля 2019

Все, что вы ожидаете, может быть достигнуто, как показано ниже,

student_data.values.map do |z|
  z.group_by { |x| x[:student_id] }.transform_values do |v|
    { 
      points: v.map { |x| x[:points] }.sum, # sum of points
      grade: (v.map { |x| x[:grade] }.sum/v.count.to_f).round(2) # average of grades
    }
  end
end

Поскольку точный ожидаемый формат вывода не указан, полученный следующим образом,

=> [
  {"ST4"=>{:points=>35, :grade=>4.67}},
  {"ST1"=>{:points=>85, :grade=>3.0}},
  {"ST2"=>{:points=>75, :grade=>3.0}},
  {"ST3"=>{:points=>55, :grade=>3.5}}
]
0 голосов
/ 02 февраля 2019

Для Рубин 2,6 с использованием Object#then или Object#yield_self для Рубин 2,5

student_data.transform_values { |st| st
  .each_with_object(Hash.new(0)) { |h, hh|  hh[:sum_points] += h[:points]; hh[:sum_grade] += h[:grade]; hh[:count] += 1.0 }
  .then{ |hh| {tot_points: hh[:sum_points], avg_grade: hh[:sum_grade]/hh[:count] } }
}


Как это работает?

Учитывая массив для каждого учащегося:

st = [{:student_id=>"ST4", :points=> 5, :grade=>5}, {:student_id=>"ST4", :points=>10, :grade=>4}, {:student_id=>"ST4", :points=>20, :grade=>5}]

Сначала создайте хэш, добавляя и считая, используя Enumerable#each_with_object с Hash#default, установленным на ноль (Hash.new(0))

step1 = st.each_with_object(Hash.new(0)) { |h, hh|  hh[:sum_points] += h[:points]; hh[:sum_grade] += h[:grade]; hh[:count] += 1.0 }
#=> {:sum_points=>35, :sum_grade=>14, :count=>3.0}

Тогда используйте тогда!(yield_self для Ruby 2.5)

step2 = step1.then{ |hh| {tot_points: hh[:sum_points], avg_grade: hh[:sum_grade]/hh[:count] }}
#=> {:tot_points=>35, :avg_grade=>4.666666666666667}

Соберите все вместе, используя Hash#transform_values, как в первом фрагменте кода

0 голосов
/ 02 февраля 2019

Требуемый хэш может быть получен таким образом.

student_data.transform_values do |arr|
  points, grades = arr.map { |h| h.values_at(:points, :grade) }.transpose
  { :points=>points.sum, :grades=>grades.sum.fdiv(grades.size) }
end
  #=> {"ST4"=>{:points=>35, :grades=>4.666666666666667},
  #    "ST1"=>{:points=>85, :grades=>3.0},
  #    "ST2"=>{:points=>75, :grades=>3.0},
  #    "ST3"=>{:points=>55, :grades=>3.5}} 

Первое значение, переданное блоку, является значением первого ключа, 'ST4', и переменной блока arr присваивается это значение:

a = student_data.first
  #=> ["ST4",
  #    [{:student_id=>"ST4", :points=> 5, :grade=>5},
  #     {:student_id=>"ST4", :points=>10, :grade=>4},
  #     {:student_id=>"ST4", :points=>20, :grade=>5}]
  #   ] 
arr = a.last
  #=> [{:student_id=>"ST4", :points=> 5, :grade=>5},
  #    {:student_id=>"ST4", :points=>10, :grade=>4},
  #    {:student_id=>"ST4", :points=>20, :grade=>5}]

Расчеты блока выполняются следующим образом.Первое значение arr, переданное map во внутренний блок, равно

h = arr.first
  #=> {:student_id=>"ST4", :points=>5, :grade=>5} 
h.values_at(:points, :grade)
  #=> [5, 5] 

После того, как оставшиеся два элемента arr переданы в блок, мы имеем

b = arr.map { |h| h.values_at(:points, :grade) }
  #=> [[5, 5], [10, 4], [20, 5]] 

Тогда

points, grades = b.transpose
  #=> [[5, 10, 20], [5, 4, 5]] 
points
  #=> [5, 10, 20] 
grades
  #=> [5, 4, 5] 

Теперь мы просто формируем хеш, который является значением 'ST4'.

c = points.sum
  #=> 35 
d = grades.sum
  #=> 14 
e = grades.size
  #=> 3 
f = c.fdiv(d)
  #=> 4.666666666666667 

Значение 'ST4' в student_data, следовательно, отображается в хеш

{ :points=>c, :grades=>f }
  #=> {:points=>35, :grades=>4.666666666666667} 

Отображения остальных ключей student_data вычисляются аналогично.

См. Hash # transform_values ​​, Enumerable # map , Hash # values_at , Array # transpose , Array # sum и Integer # fdiv .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...