Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have a Ruby array of objects, how to change this array into multiple arrays based on the property of those objects?

What's the best way to change a big array into multiple sub arrays based on the property of the objects in the original array? For example I have an array of objects(all objects have the same properties):

array = [
    {:name => "Jim", :amount => "20"},
    {:name => "Jim", :amount => "40"},
    {:name => "Jim", :amount => "30"},
    {:name => "Eddie", :amount => "7"},
    {:name => "Eddie", :amount => "12"},
    {:name => "Pony", :amount => "330"},
    {:name => "Pony", :amount => "220"},
    {:name => "Pony", :amount => "50"}
]

Note that all objects with the same name property are consecutive in the array. Now I want to group the objects into sub arrays based on the name property. What I need is:

result = [
    [
        {:name => "Jim", :amount => "20"},
        {:name => "Jim", :amount => "40"},
        {:name => "Jim", :amount => "30"}
    ],
    [
        {:name => "Eddie", :amount => "7"},
        {:name => "Eddie", :amount => "12"}
    ],
    [
        {:name => "Pony", :amount => "330"},
        {:name => "Pony", :amount => "220"},
        {:name => "Pony", :amount => "50"}
    ]
]

What's the best way to do this? Thanks.

like image 402
Just a learner Avatar asked Jan 27 '26 12:01

Just a learner


2 Answers

Use group_by for the heavy lifting and then map to pull out what you want:

result = array.group_by { |h| h[:name] }.map { |k, v| v }

For example:

>> results = array.group_by { |h| h[:name] }.map { |k, v| v }
>> pp results
[[{:name=>"Jim", :amount=>"20"},
  {:name=>"Jim", :amount=>"40"},
  {:name=>"Jim", :amount=>"30"}],
 [{:name=>"Eddie", :amount=>"7"},
  {:name=>"Eddie", :amount=>"12"}],
 [{:name=>"Pony", :amount=>"330"},
  {:name=>"Pony", :amount=>"220"},
  {:name=>"Pony", :amount=>"50"}]]

You could also skip the map and go straight to Hash#values:

result = array.group_by { |h| h[:name] }.values

Thanks go to KandadaBoggu for pointing out this oversight.

like image 143
mu is too short Avatar answered Jan 29 '26 04:01

mu is too short


If hashes with the same :name value are always consecutive elements you can do it like that:

result = array.each_with_object([]) do |e, memo|
  if memo.last && memo.last.last[:name] == e[:name]
    memo.last << e
  else
    memo << [e] 
  end 
end

or you can use Enumerable#chunk (again, taking into account that elements with the same :name value are consecutive):

result = array.chunk{ |e| e[:name] }.map(&:last)
like image 41
KL-7 Avatar answered Jan 29 '26 04:01

KL-7



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!