Charles Dayman
Charles Dayman

Reputation: 5

Consolidating a Ruby Array of Hashes

I'm interning at a company right now, and I have a database that I connect to in order to get some specific data about our customers. The query is all worked out, the database is returning the data I need, but now I need to figure out how to consolidate the data into the necessary format. We are using Ruby 3.1.2 for reference.

This is the format that I receive from our database (the real data will be much larger, so I'm only using a small dataset until the logic is solid).

[{ "product_id"=>1, "customer_id"=>001 },
 { "product_id"=>2, "customer_id"=>001 },
 { "product_id"=>1, "customer_id"=>002 },
 { "product_id"=>3, "customer_id"=>002 },
 { "product_id"=>1, "customer_id"=>003 },
 { "product_id"=>2, "customer_id"=>003 },
 { "product_id"=>3, "customer_id"=>003 }]

When I get this data, I need to get a list of each distinct "customer_id" with a list of every "product_id" they have assigned to them. Example of what I need to get back below.

{001=>[1, 2], 002=>[1, 3], 003=>[1, 2, 3]}

I thought I had a solution with the line below, but it doesn't seem to work how I expected.

data.group_by(&:customer_id).transform_values { |p| p.pluck(:product_id) }

Upvotes: 0

Views: 68

Answers (3)

Jignesh Gohel
Jignesh Gohel

Reputation: 6552

The other answers are correct in their own ways but in my opinion code-readability should also always be considered and considering that factor the accepted answer looks more nearer and my solution below is also based on the same logic with the only difference that I have moved out the string keys outside the loop because the string literal inside the loop should create, in each iteration, different String instances in memory for those keys.

array = [{ "product_id"=>1, "customer_id"=>001 },
         { "product_id"=>2, "customer_id"=>001 },
         { "product_id"=>1, "customer_id"=>002 },
         { "product_id"=>3, "customer_id"=>002 },
         { "product_id"=>1, "customer_id"=>003 },
         { "product_id"=>2, "customer_id"=>003 },
         { "product_id"=>3, "customer_id"=>003 }]

transformed_data = {}

customer_id_key = "customer_id"
product_id_key = "product_id"

array.each do |h|
  customer_id = h[customer_id_key]
  product_id = h[product_id_key]

  transformed_data[customer_id] ||= []
  transformed_data[customer_id] << product_id
end 

transformed_data

Upvotes: 0

Chris
Chris

Reputation: 36611

In addition to #group_by one might use #each_with_object to iteratively build the needed hash.

data.each_with_object({}) { |x, h| 
  h[x["customer_id"]] ||= []
  h[x["customer_id"]] << x["product_id"] 
}
# => {1=>[1, 2], 2=>[1, 3], 3=>[1, 2, 3]}

Upvotes: 2

spickermann
spickermann

Reputation: 106972

I would do this:

array = [{ "product_id"=>1, "customer_id"=>001 },
         { "product_id"=>2, "customer_id"=>001 },
         { "product_id"=>1, "customer_id"=>002 },
         { "product_id"=>3, "customer_id"=>002 },
         { "product_id"=>1, "customer_id"=>003 },
         { "product_id"=>2, "customer_id"=>003 },
         { "product_id"=>3, "customer_id"=>003 }]

array.group_by { |hash| hash['customer_id'] }
     .transform_values { |values| values.map { |value| value['product_id'] } }
#=> { 1 => [1, 2], 2 => [1, 3], 3 => [1, 2, 3] }

Upvotes: 2

Related Questions