Wednesday, 20 May 2015

Rails Active Record .median

Rails neatly provides an .average method to easily find the average value of a column on your db, but it doesn't provide one for finding the median. Thankfully it's really easily to implement

If you just want to add it to one class, put this into the model:

def self.median(column_name)
  median_index = (count / 2)
  # order by the given column and pluck out the value exactly halfway
  order(column_name).offset(median_index).limit(1).pluck(column_name)[0]
end

If you want it to work across all Active Record models in your db... you could add it as a concern (then include it in every class you want it in) or you could just extend ActiveRecord::Base (so it's available to every Active Record, just like .average).

If you want to do that, add the following to /config/initializers/active_record.rb

class ActiveRecord::Base
  def self.median(column_name)
    median_index = (count / 2)
    # order by the given column and pluck out the value exactly halfway
    order(column_name).offset(median_index).limit(1).pluck(column_name)[0]
  end
end

Note: I've tested it even with chained scopes and it seems to work ok on our project (this counts as a final - ie you can't chain after it)

Note: as pointed out to me, the median of an even number of things is the average of the middle two values... and this version doesn't do that - this is a quick-and-dirty version. Feel free to adapt to be mathematically correct if you need that level of precision