Reputation: 1433
Note: There were a few similar questions on SO about this, like here and here, but none seem quite like what I'm looking for.
Say I have an array of hashes like this:
arr_with_dup_hsh_keys = [
{ foo: "dup", bar: 1 },
{ foo: "dup", bar: 2 },
{ foo: "dup", bar: 3 },
{ foo: "dup", bar: 4 },
{ foo: "dup", bar: 5 }
]
How do I reduce that down to this?
{ foo: "dup", bars: [1, 2, 3, 4, 5] }
And what if there are different values for foo
?
arr_with_dup_hsh_keys = [
{ foo: "dup", bar: 1 },
{ foo: "dup", bar: 2 },
{ foo: "soup", bar: 3 },
{ foo: "dup", bar: 4 },
{ foo: "soup", bar: 5 }
]
Upvotes: 1
Views: 1113
Reputation: 110675
def combine(arr)
arr.group_by {|g|g[:foo]}.map {|_,a|{foo: a.first[:foo], bar: a.map {|g| g[:bar]}}}
end
combine arr_with_dup_hsh_keys
#=> [{:foo=>"dup", :bar=>[1, 2, 3, 4, 5]}]
arr_with_dup_hsh_keys1 = [
{ foo: "dup", bar: 1 },
{ foo: "dup", bar: 2 },
{ foo: "soup", bar: 3 },
{ foo: "dup", bar: 4 },
{ foo: "soup", bar: 5 }
]
combine arr_with_dup_hsh_keys1
#=> [{:foo=>"dup", :bar=>[1, 2, 4]}, {:foo=>"soup", :bar=>[3, 5]}]
See Enumerable#group_by and note that
arr_with_dup_hsh_keys1.group_by { |g| g[:foo] }
#=> {"dup"=> [{:foo=>"dup", :bar=>1}, {:foo=>"dup", :bar=>2},
# {:foo=>"dup", :bar=>4}],
# "soup"=>[{:foo=>"soup", :bar=>3}, {:foo=>"soup", :bar=>5}]}
You could alternatively write the following.
def combine(arr)
arr.each_with_object({}) do |g,h|
f = g.merge(bar: [g[:bar]])
h.update(f[:foo]=>f) { |_,o,n| { foo: o[:foo], bar: o[:bar]+n[:bar] } }
end.values
end
combine arr_with_dup_hsh_keys1
#=> [{:foo=>"dup", :bar=>[1, 2, 4]}, {:foo=>"soup", :bar=>[3, 5]}]
This uses the form of Hash#update (aka merge!
) that employs a block to determine the values of keys that are present in both hashes being merged. See the doc for an explanation of the three block variables (the first being the common key, which I've represented with an underscore to signify that it's not used in the block calculation).
Upvotes: 4
Reputation: 2984
This is what I came up:
a = [
{ foo: "dup", bar: 1 },
{ foo: "dup", bar: 2 },
{ foo: "dup", bar: 3 },
{ foo: "dup", bar: 4 },
{ foo: "dup", bar: 5 }
]
h = {}
a.map(&:keys).uniq.flatten.each_with_index do |key, idx|
h[key] = a.map(&:values).collect { |a| a[idx]}.uniq
end
h
#=> {:foo=>["dup"], :bar=>[1, 2, 3, 4, 5]}
Upvotes: 1
Reputation: 106027
If your data is really as simple as in your question, this will do what you want:
{ foo: "dup",
bars: arr_with_dup_hsh_keys.map {|hsh| hsh[:bar] }
}
Upvotes: 1