wslater
wslater

Reputation: 1

How can I convert an array of ActiveRecord Relations into a hash that is keyed off of one of the parameters?

I'm creating a raw SQL upsert and I have a fairly large array of ActiveRecord Relations that I need to convert to a hash. I need the hash to be keyed off of one of the parameters that I'm selecting so I can quickly get at the value.

I found this response using as_json and I can almost get what I need using that, but its not quite there.

profiles = Profile.all.select(:id, :foo)  #returns an array of ActiveRecord Relations
profiles = profiles.as_json

Yields

{:id => 123, :foo => "bar"}
{:id => 456, :foo => "baz"}

But what I'd like is

{123 => "bar", 456 => "baz"}

I realize that I could map the results of as_json to a new hash, but I have to run this fairly frequently on a few million records. This is also a small part of a larger rake task, and I'd like to keep looping over all of the records down to minimum.

My reason for doing this all manually instead of letting ActiveRecord handle it was that it was initially taking over 24 hours to run the task, and even using activerecord-import only sped it up to ~12 hours. I chose to go with using raw sql based off the benchmarks made in this blogpost

Upvotes: 0

Views: 2861

Answers (4)

Santhosh
Santhosh

Reputation: 29124

In Rails4, you can do

Profile.pluck(:id, :foo).to_h

Upvotes: 5

pangpang
pangpang

Reputation: 8821

You can use select_all method get an array of hash instead of ActiveRecord object, it is more faster and less memory consumption.

Profile.connection.select_all("select id, foo from profiles;")
=> [{"id" => xxx, "foo" => xxx}, {}, .....]

then convert the result to your desired format.

Upvotes: 1

mu is too short
mu is too short

Reputation: 434635

I'd probably bypass ActiveRecord and slurp the data straight out of the database into a Hash with something like this:

Profile.connection
       .execute('select id, foo from profiles')
       .each_with_object({}) { |row, h| h[row['id'].to_i] = row['foo'] }

Or better (IMO), bypass Rails and Ruby too and do everything you need to do inside the database with SQL, temp tables, etc. When you're working with large amounts of data, your best bet is usually to ignore your application completely and do everything inside the database; databases are great at working with large piles of data, that's what they do.

Upvotes: 0

DustinFisher
DustinFisher

Reputation: 396

On your profile model you can do this to get your desired result:

  def self.to_hash
    Hash[*all.map{|p| [p.id, p.foo]}.flatten]
  end

You can then call: Profile.select(:id, :foo).to_hash

Upvotes: 0

Related Questions