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.

±—±--------+
| 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?

Hi –

On Thu, 13 Mar 2008, Devin M. wrote:

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


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!

On Mar 12, 10:45 pm, Devin M. [email protected]
wrote:

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?

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.

Person.find(:all).to_h {|p| [p.id, p.name]}

Person.find(:all).to_h :id, :name

(params.keys - ignore).to_h {|k| params[k]}

Person.find(:all).to_h(:name).invert

Inspired from

http://www.chadfowsler.com/2007/8/3/enumerable-injecting

but simplified and then made complicated again. :slight_smile:

def to_h(key=nil, value=nil, &block)
block = lambda do |i|
if value.nil?
i.send key
else
[i.send(key), i.send(value)]
end
end unless block_given?
inject Hash.new do |map, i|
k, v = *[block.call(i)].flatten
if v.nil?
v = k
k = i
end
map[k] = v
map
end
end
end

Eric

@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:

Oops. Lazy ring finger. :slight_smile:

Hi –

On Thu, 13 Mar 2008, AndyV wrote:

a fan of inject-ing things. :slight_smile:
Yes, that runs faster, though the code needs some massaging to run:

a.inject({}) {|collection, item|
collection.merge(item.id => item.year)
}

David


Upcoming Rails training from David A. Black and Ruby Power and Light:
ADVANCING WITH RAILS, April 14-17 2008, New York City
CORE RAILS, June 24-27 2008, London (Skills Matter)
See http://www.rubypal.com for details. Berlin dates coming soon!