March 13th, 2009

Distinct Values

module ActiveRecord #mixin for active record
  module ModelHelper # module name
    def self.included(base) # include this into the base of the class instance
      base.class_eval do # evaluate the below
        (class << self;self;end).class_eval do
          def self.distinct_values(column)
            find(:all, :select => "DISTINCT #{column}").map{|x| x.send(column)} if column_names.include?(column)
          end
        end
      end
    end
  end
end

I like to setup a file in the /lib directory to hold model_helper.rb. I tend to put functions that I like to mix into all of my models into this file. This keeps things dry and it prevents me from running all over wondering where a certain active record model function is located.

def self.distinct_values(column)
  find(:all, :select => "DISTINCT #{column}").map{|x| x.send(column)} if column_names.include?(column)
end

After I mix this function in to all the active records objects I’m give the ability to call it like this:

distinct_items = Model.distinct_values("column_name")
#> ["value1","value2","value3"]

This will return distinct values of a model, instead of the objects themselves. I find this useful in projects where there is sometimes an intentional lack of normalization on a table.

table: posts
+-----+----------+
| id  | category |
+-----+----------+
|  5  | ruby     |
|  35 | ruby     |
|  52 | news     |
|  53 | news     |
|  63 | news     |

so now we can just call.

  Post.distinct_values("category")
  #-> ["ruby","news"]

This is useful for filling dropdown lists, select boxes, etc.

6 Responses to “Distinct Values”

  1. Charles Says:

    So by doing this:

    def self.included(base) # include this into the base of the class instance

    The method is defined in the parent class for all extending classes. And you can easily override / extend this method in all those extending classes?

  2. Jim Says:

    in the case of ActiveRecord module (its already being included). When I extend my ‘base’ which is the where this module is included. I believe in ActiveRecord::Base. Then all inherited classes and there instances receive this method.

  3. Charles Says:

    ok, cool i think that answers my very vague question. By saying you “mix in” this method it means the base object has the method, so all other models can easily override or extend that method.

  4. linhe Says:

    Thanks for the post. Nice thoughts!

    one question regarding this part of the code

    (class < “DISTINCT #{column}”).map{|x| x.send(column)} if column_names.include?(column)
    end
    end

    should it be without ’self.’ here? def distinct_values(column) … the eigen class of ActiveRecord would be Class#ActiveRecord. maybe I’m missing something? :)

  5. linhe Says:

    sorry for post again, it seems I forgot to format the code plz clean it up for me.

            (class < "DISTINCT #{column}").map{|x| x.send(column)} if column_names.include?(column)
              end
            end
    
  6. Jim Says:

    Without testing, I’m not entirely sure if it would work or not. However, I know it does work with self.distinct_values. I placed self. because this is a class method mixed into the ActiveRecord Class. All inheritied classes will now have this method. So this will allow Child <(of) ActiveRecord::Base to call distinct values.

Leave a Reply