I have an array of hashes called array_of_hash:
array_of_hash = [
{:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"},
{:name=>"5", :address=>"UK", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC"},
{:name=>"6", :address=>"CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"CD"},
{:name=>"29", :address=>"GERMANY", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE"},
{:name=>"30", :address=>"CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"FG"}
]
I wish to group these hashes by consecutive value of the key :name. The first group would be "1" alone since there is no key with :name => "1".succ #=> "2". The second group would contain hashes with values of "5" and "6". The third group would be the last two hashes in the array, for which :name=>29 and :name=>30.
My desired array of hashes should look like this:
[
{:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"},
{:name=>"5-6", :address=>"UK,CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC,CD"},
{:name=>"29-30", :address=>"GERMANY,CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE, FG"},
]
Use case II
array_of_hash = [
{:name=>"1", :address=>"USA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB"},
{:name=>"2", :address=>"UK", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"BC"},
{:name=>"3", :address=>"CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"CD"},
{:name=>"29", :address=>"GERMANY", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE"},
{:name=>"30", :address=>"CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"FG"}
]
Desired result for use case II
[
{:name=>"1-3", :address=>"USA,UK,CANADA", :collection=>["LAND", "WATER", "OIL", "TREE", "SAND"], :sequence=>"AB,BC,CD"},
{:name=>"29-30", :address=>"GERMANY,CHINA", :collection=>["LAPTOP", "SHIP", "MOUNTAIN"], :sequence=>"DE, FG"},
]
What I did so far:
new_array_of_hashes = []
new_array_of_hashes << { name: array_of_hashes.map {|h| h[:name].to_i}} << {address: array_of_hashes.map {|h| h[:address]}} << {collection: array_of_hashes.map {|h| h[:collection]}} << {sequence: array_of_hashes.map {|h| h[:sequence]}}
[{:name=>[1, 5, 6, 29, 30]},
{:address=>["USA", "UK", "CANADA", "GERMANY", "CHINA"]},
{:collection=>
[["LAND", "WATER", "OIL", "TREE", "SAND"],
["LAND", "WATER", "OIL", "TREE", "SAND"],
["LAND", "WATER", "OIL", "TREE", "SAND"],
["LAPTOP", "SHIP", "MOUNTAIN"],
["LAPTOP", "SHIP", "MOUNTAIN"]]},
{:sequence=>["AB", "BC", "CD", "DE", "FG"]}]
I am only able to combine it.
First, let's make an array of the groups that we ultimately want. We'll use Ruby's
Array#slice_whenmethod, which iterates over an array with the current and next array element, allowing us to compare the two. Our conditional will instruct Ruby to slice the array if the names (converted to integers) are not sequential or if the collections are not identical.But because you are using Ruby 2.1, you'll need to use
slice_beforeand use local variables to keep track of previous elements. Per the documentation, we can accomplish this by first priming a local variable:and then resetting it and a second local variable as we iterate over the array:
In either case,
groupsshould now look like this:Now we take the resulting array and map its elements to a new hash, formatted as you specified.
For
:name, we take the first and last elements of the group, call.uniqto eliminate duplicates, and join them with a hyphen. (If only one element exists,joinreturns the single element unchanged.)For
:collection, we simply use the collection found in the first element of the group.For
:sequence, we join the sequences of each element of the group with a comma. (Again, single elements are returned unchanged.)