Decado
Decado

Reputation: 356

ruby db result set to array in a hash in a hash

I have a db query which returns results like:

db_result.each {|row| puts row}
{"IP"=>"1.2.3.4","Field1"=>"abc","Field2"=>"123"}
{"IP"=>"1.2.3.4","Field1"=>"abc","Field2"=>"234"}
{"IP"=>"1.2.3.4","Field1"=>"bcd","Field2"=>"345"}
{"IP"=>"3.4.5.6","Field1"=>"bcd","Field2"=>"456"}
{"IP"=>"3.4.5.6","Field1"=>"bcd","Field2"=>"567"}

And want to put it into a hash like:

{
  "1.2.3.4" => {
    "abc" => ["123", "234"],
    "bcd" => "345"
  },
  "3.4.5.6" => {
    "bcd" => ["456", "567"]
  }
}

What I am currently doing is:

result_hash = Hash.new { |h, k| h[k] = {} }
db_result.each do |row|
  result_hash[row["IP"]] = Hash.new { |h, k| h[k] = [] } unless result_hash.has_key? row["IP"]
  result_hash[row["IP"]][row["Field1"]] <<  row["Field2"]
end 

Which works, however was wondering if there is a neater way.

Upvotes: 2

Views: 1266

Answers (2)

the Tin Man
the Tin Man

Reputation: 160551

Consider this a peer-review. As a recommendation for processing and maintenance...

I'd recommend the data structure you want be a little more consistent.

Instead of:

{
  "1.2.3.4" => {
    "abc" => ["123", "234"],
    "bcd" => "345"
  },
  "3.4.5.6" => {
    "bcd" => ["456", "567"]
  }
}

I'd recommend:

{
  "1.2.3.4" => {
    "abc" => ["123", "234"],
    "bcd" => ["345"]
  },
  "3.4.5.6" => {
    "abc" => [],
    "bcd" => ["456", "567"]
  }
}

Keep the same keys in each sub-hash, and make the values all be arrays. The code for processing that overall hash will be more straightforward and easy to follow.

Upvotes: 1

Serge Balyuk
Serge Balyuk

Reputation: 3462

I agree with Michael, there is nothing wrong with your method. The intent behind the code can be easily seen.

If you want to get fancy, here's one (of many) ways to do it:

x = [
  {"IP"=>"1.2.3.4","Field1"=>"abc","Field2"=>"123"},
  {"IP"=>"1.2.3.4","Field1"=>"abc","Field2"=>"234"},
  {"IP"=>"1.2.3.4","Field1"=>"bcd","Field2"=>"345"},
  {"IP"=>"3.4.5.6","Field1"=>"bcd","Field2"=>"456"},
  {"IP"=>"3.4.5.6","Field1"=>"bcd","Field2"=>"567"}
]


y = x.inject({}) do |result, row|
  new_row = result[row["IP"]] ||= {}
  (new_row[row["Field1"]] ||= []) << row["Field2"]

  result
end

I think this should yield the same time complexity as your method.

Upvotes: 1

Related Questions