How to order ActiveRecord query based on calculations

104 views Asked by At

I'd like to list bakeries by the number pastries they have on sale, say.

To rank them, I would sum the multiplication of the number of pastries available of each pastry, by the percentage they are on sale. Bakery could have 0-10 pastries on sale. For example:

bakery1.pastry1 = 100
bakery1.pastry1_discount = 10 (%)
bakery1.pastry2 = 50
bakery1.pastry2_discount = 20 (%)
(2 pastries on sale)

(100 x 10) + (50 x 20) = 2000 .. would be listed first

bakery2.pastry1 = 10
bakery2.pastry1_discount = 40
bakery2.pastry2 = nil
bakery2.pastry2_discount = nil
(only 1 pastry on sale, etc)

10 x 40 = 400 .. listed 2nd

bakery3.pastry1 = 10
bakery3.pastry1_discount = 20
bakery3.pastry2 = nil
bakery3.pastry2_discount = nil

10 x 20 = 200 .. listed last

So how do I order Bakery by the sum of the multiplication of certain attribute values?

I understand thus far that sort_by is heavy on the memory of the database.

UPDATE: ... it'd have to be (pastries available - pastries sold) x discount

2

There are 2 answers

4
Akeel Ahmed Qureshi On

As I understand the problem. Here is the solution to this,

class Bakery
  attr_accessor :name, :pastries

  def initialize(name, pastries)
    @name = name
    @pastries = pastries
  end

  def order_value
    pastries.sum { |_, values| values[:pastry].nil? ? 0 : values[:pastry] * values[:discount] }
  end

  def self.order(*bakeries)
    bakeries.max_by(&:order_value)
  end
end

Example

bakery1 = Bakery.new("Bakery1", { pastry1: { pastry: 100, discount: 10 },pastry2: { pastry: 50, discount: 20 } })
bakery2 = Bakery.new("Bakery2", { pastry1: { pastry: 10, discount: 40 } })
bakery3 = Bakery.new("Bakery3", { pastry1: { pastry: 10, discount: 20 } })

highest_order_bakery = Bakery.order(bakery1, bakery2, bakery3)

puts "#{highest_order_bakery.name}: #{highest_order_bakery.order_value} order value"

highest_order_bakery.pastries.sort_by { |_, values| -values[:discount] }.each do |pastry, values|
 puts "  #{pastry}: #{values[:discount]}% discount"
end
6
WALVISK On

i will use sort_by instead of order and made an instance method to calculate available pastries

class Bakery
 # method to calculate pastery availability
 def pastery_count
   count = 0
   self.pastery.each do |ps|
     count = count + ps.count.to_i * ps.discount.to_i
   end

   count
 end
end

then call with sort_by

Bakery.sort_by{|br| br.pastery_count}

you can also combine with where clause

Bakery.where(...).sort_by{|br| br.pastery_count}