1

I have the ruby array as following :

array = [{"id"=>8, "book_id"=>14238}, {"id"=>5, "book_id"=>14238}, {"id"=>7, "book_id"=>10743}, {"id"=>9, "book_id"=>10743}]

I want a new array combining the result of ids having same book_id.

Expected Result:

array = [{"book_id"=>14238, "id"=>[8,5]}, {"book_id"=>10743, "id"=>[7,9]}]

3 Answers 3

2

I can't say that this is easy to understand, but it is concise:

array.group_by {|item| item["book_id"] }.map do |k, v|
  { "book_id" => k, "id" => v.map {|item| item["id"] } }
end

=> [{"book_id"=>14238, "id"=>[8, 5]}, {"book_id"=>10743, "id"=>[7, 9]}]

The first transformation done by group_by rearranges your array so that items with the same book_id are grouped together:

array.group_by {|item| item["book_id"] }
 => {14238=>[{"id"=>8, "book_id"=>14238}, {"id"=>5, "book_id"=>14238}], 10743=>[{"id"=>7, "book_id"=>10743}, {"id"=>9, "book_id"=>10743}]}

The second transformation (map) reformats the hash produced by the group_by into a list of hashes, and the second map collects the id's into a list.

Sign up to request clarification or add additional context in comments.

2 Comments

I copied my solution from irb, to paste here and I saw my answer was exactly as yours +1
my answer was very close to yours same as the Tin Man I have deleted bcz it was not satisfied expected output. 1+
1

You can also do this using the form of Hash#update (a.k.a. merge!) that employs a block to resolve the values of keys that are contained in both of the hashes being merged.

Code

def aggregate(arr)
  arr.each_with_object({}) do |g,h|
    f = { g["book_id"]=>{ "id"=>[g["id"]], "book_id"=>g["book_id"] } }
    h.update(f) do |_,ov,nv|
      ov["id"] << nv["id"].first
      ov
    end
  end.values
end

Example

arr = [{"id"=>8, "book_id"=>14238}, {"id"=>5, "book_id"=>14238},
       {"id"=>7, "book_id"=>10743}, {"id"=>9, "book_id"=>10743},
       {"id"=>6, "book_id"=>10511}]

aggregate(arr)
  #=> [{"id"=>[8, 5], "book_id"=>14238},
  #    {"id"=>[7, 9], "book_id"=>10743},
  #    {"id"=>[6],    "book_id"=>10511}]

Alternative output

Depending on your requirements, you might consider building a single hash instead of another array of hashes:

def aggregate(arr)
  arr.each_with_object({}) { |g,h|
    h.update({ g["book_id"]=>[g["id"]] }) { |_,ov,nv| ov+nv } }
end

aggregate(arr)
  #=> {14238=>[8, 5], 10743=>[7, 9], 10511=>[6]}

Comments

0

I'd use a hash for the output for easier lookups and/or reuse:

array = [{"id"=>8, "book_id"=>14238}, {"id"=>5, "book_id"=>14238}, {"id"=>7, "book_id"=>10743}, {"id"=>9, "book_id"=>10743}]

hash = array.group_by{ |h| h['book_id'] }.map{ |k, v| [k, v.flat_map{ |h| h['id'] }]}.to_h
# => {14238=>[8, 5], 10743=>[7, 9]}

The keys are the book_id values, and the associated array contains the id values.


The expected result of

array = [{"book_id"=>14238, "id"=>[8,5]}, {"book_id"=>10743, "id"=>[7,9]}]

isn't a good structure if you're going to do any sort of lookups in it. Imagine having hundreds or thousands of elements and needing to find "book_id" == 10743 in the array, especially if it's not a sorted list; The array would have to be walked until the desired entry was found. That is a slow process.

Instead, simplify the structure to a simple hash, allowing you to easily locate a value using a simple Hash lookup:

hash[10743]

The lookup will never slow down.

If the resulting data is to be iterated in order by sorting, use

sorted_keys = hash.keys.sort

and

hash.values_at(*sorted_keys)

to extract the values in the sorted order. Or iterate over the hash if the key/values need to be extracted, perhaps for insertion into a database.

2 Comments

This is not the expected output
That's correct. Sometimes the expected output is not the best way to work with data. Stack Overflow isn't just about returning the expected output, it's also about educating that there are simpler ways of doing things that accomplish the same results. Consider how easily the expected output could be searched or reused, vs. a simple hash. The expected output has to be iterated just to find a particular book_id, which would grow slower and slower as the initial array grows. That's hardly efficient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.