7

So, I have an after_save hook on review model which calls calculate_specific_rating function of product model. The function goes like this:

def calculate_specific_rating
    ratings = reviews.reload.all.pluck(:rating)
    specific_rating = Hash.new(0)
    ratings.each { |rating| specific_rating[rating] += 1 }
    self.specific_rating = specific_rating
    save
  end

Right now, it returns

specific_rating => { 
"2"=> 3, "4"=> 1
}

I want it to return like:

specific_rating => { 
"1"=> 0, "2"=>3, "3"=>0, "4"=>1, "5"=>0
}

Also, is it okay to initialize a new hash everytime a review is saved? I want some alternative. Thanks

roshita
  • 175
  • 4

2 Answers2

6

You can create a range from 1 until the maximum value in ratings plus 1 and start iterating through it, yielding an array where the first element is the current one, and the second element is the total of times the current element is present in ratings. After everything the result is converted to a hash:

self.specific_rating = (1..ratings.max + 1).to_h { |e| [e.to_s, ratings.count(e)] }
save
Sebastián Palma
  • 32,692
  • 6
  • 40
  • 59
  • thanks !! What should we do to initialize the hash values to zero when the product is first created. and to use the above function once the rating is created on that product – roshita Mar 01 '20 at 14:44
  • You can do that in the migration, adding a default value for that column when the record is created @twinkle. – Sebastián Palma Mar 01 '20 at 16:03
1

You could also do something like this -

def calculate_specific_rating
  ratings = [1,2,3,4,5]
  existing_ratings = reviews.group_by(&:rating).map{|k,v| [k, v.count]}.to_h
  Hash[(ratings - existing_ratings.keys).map {|x| [x, 0]}].merge(existing_ratings)
end

which gives

{3=>0, 4=>0, 5=>0, 2=>3, 1=>1}

Gautam
  • 1,754
  • 1
  • 14
  • 22