Convert find(:all) to hash with id as key

In one of my applications, I have a lot of queries that return just an
id and a content column for each of the rows in the result. e.g.

Hi --

In one of my applications, I have a lot of queries that return just an
id and a content column for each of the rows in the result. e.g.

+----+---------+
> id | content |
+----+---------+
> 35 + apple |
> 79 + pear |
+----+---------+

I'd like to be able to access the object returned by
  result = Food.find(:all)
like
  result[79] = "pear"

I'm trying to minimize the time needed to convert the Food object into
this hash. Does anyone have an idea which method might be the fastest?

You may already have one that's faster than either of these, but for
what it's worth, it looks like this:

   a = Food.find(:all, :select => "id, content")
   result = Hash[*a.map {|e| e.attributes.values}.flatten]

is much slower than:

   a = Food.find(:all, :select => "id, content")
   result = {}
   a.map {|e| result[e.id] = e.content }

Much slower, as in:

             user system total real
Hash 5.820000 0.030000 5.850000 ( 5.849504)
map 0.760000 0.000000 0.760000 ( 0.763521)

I think the first is cooler, but coolth sometimes comes at a price :slight_smile:

David

If you really want to be fast you might avoid the object instantiation
and go with direct SQL. But if you don't have to be super fast I would
keep it readable and just do something like this:

food_lookup_table = Food.find(:all).inject({}) {|m,f| m[f.id] =
f.content; m}

Of course over time I have crafted a nice Enumerable#to_h method to
handle many of these common cases so the above translated to:

food_lookup_table = Food.find(:all).to_h :id, :content

If you are interested in my method I have pasted it below. It does a
lot more than just the above because I have continued to enhance it
over time.

module Enumerable
  # Abstracts the process of converting a list to a hash. In your
block
  # just return a array where the first value is the key and the
second
  # value is the value. If the key and value are simple method calls
  # you can just pass in the symbols as arguments. Finally if you just
  # return a single value (not an array) then it will assume that is
  # the value and use the enumerable item as the key. If the value is
  # a simple method and you want to assume the key you can just pass
  # the value method in.

@David,

I don't have your datasest... how does the following compare?

a = Food.find(:all, :select=>'id, content')
a.inject({}){|collection, item| collection.merge
item;id=>item.content}

Perhaps as a result of all the PED talk in baseball, I've become quite
a fan of inject-ing things. :slight_smile:

Hi --

Oops. Lazy ring finger. :slight_smile: