Group array of hashes by key
Use group_by
.
array.group_by{|h| h[:user]}.values
How can I group this array of hashes?
The &:age
means that the group_by
method should call the age
method on the array items to get the group by data. This age
method is not defined on the items which are Hashes in your case.
This should work:
array.group_by { |d| d[:age] }
Grouping an array of hashes
Refactored solution
Here's a longer but possibly better solution, with 3 helper methods :
class Array
# Remove key from array of hashes
def remove_key(key)
map do |h|
h.delete(key)
h
end
end
# Group hashes by values for given key, sort by value,
# remove key from hashes, apply optional block to array of hashes.
def to_grouped_hash(key)
by_key = group_by { |h| h[key] }.sort_by { |value, _| value }
by_key.map do |value, hashes|
hashes_without = hashes.remove_key(key)
new_hashes = block_given? ? yield(hashes_without) : hashes_without
[value, new_hashes]
end.to_h
end
# Convert array to indexed hash
def to_indexed_hash(first = 0)
map.with_index(first) { |v, i| [i, v] }.to_h
end
end
Your script can then be written as :
data.to_grouped_hash(:year) do |year_data|
year_data.to_grouped_hash(:month) do |month_data|
month_data.to_indexed_hash(1)
end
end
It doesn't need Rails or Activesupport, and returns :
{2015=>
{12=>
{1=>{:account_id=>145, :balance=>4}, 2=>{:account_id=>163, :balance=>11}}},
2016=>
{11=>
{1=>{:account_id=>134, :balance=>3}, 2=>{:account_id=>135, :balance=>0}},
12=>{1=>{:account_id=>133, :price=>5}}}}
Refinements could be use to avoid polluting the Array class.
Original one-liner
# require 'active_support/core_ext/hash'
# ^ uncomment in plain ruby script.
data.group_by{|h| h[:year]}
.map{|year, year_data|
[
year,
year_data.group_by{|month_data| month_data[:month]}.map{|month, vs| [month, vs.map.with_index(1){|v,i| [i,v.except(:year, :month)]}.to_h]}
.to_h]
}.to_h
It uses Hash#except from ActiveSupport.
It outputs :
{
2016 => {
12 => {
1 => {
:account_id => 133,
:price => 5
}
},
11 => {
1 => {
:account_id => 134,
:balance => 3
},
2 => {
:account_id => 135,
:balance => 0
}
}
},
2015 => {
12 => {
1 => {
:account_id => 145,
:balance => 4
},
2 => {
:account_id => 163,
:balance => 11
}
}
}
}
Ruby group array of hashes by numeric key
Injecting into a default hash:
arr = [{1=>6}, {1=>5}, {4=>1}]
arr.inject(Hash.new{|h,k| h[k]=[]}){|h, e| h[e.first[0]] << e.first[1]; h}
# => {1=>[6, 5], 4=>[1]}
Or, as suggested in the comments:
arr.each.with_object(Hash.new{|h, k| h[k] = []}){|e, h| h[e.first[0]] << e.first[1]}
# => {1=>[6, 5], 4=>[1]}
How to group and filter this array of hashes. Ruby
I believe this is what you need here.
result = @complete_transactions
.group_by{|hash| hash['toAddress']}
.map do |key, value|
{
'toAddress' => key,
'amount' => value.sum {|val| val['amount'].to_i},
'fromAddress' => value.map {|val| val['fromAddress']}.uniq
}
end
p result
This is for Ruby versions after 2.4.0. For prior versions, calculate the sum of amounts with this.
'amount' => value.map {|val| val['amount'].to_i}.inject(:+)
The following statement groups all the hashes by their 'toAddress' values.
.group_by{|hash| hash['toAddress']}
The followins statement iterates through each of the resulting hash and creates a new hash.
.map do |key, value|
In the following block, we create our desired hash by operating on the values of each hash.
{
'toAddress' => key,
'amount' => value.sum {|val| val['amount'].to_i},
'fromAddress' => value.map {|val| val['fromAddress']}.uniq
}
For Ruby versions prior to 2.4.0, the code becomes.
{
'toAddress' => key,
'amount' => value.map {|val| val['amount'].to_i}.inject(:+),
'fromAddress' => value.map {|val| val['fromAddress']}.uniq
}
Hope this helps.
Group by and sum array of hashes in javascript
Using an ES6 Map and reduce
:
const responseData = [{deviceType: "Smartphone", deviceCount: 14},{deviceType: "Tablet", deviceCount: 11},{deviceType: "Notebook", deviceCount: 3},{deviceType: "Desktop", deviceCount: 2},{deviceType: "Smartphone", deviceCount: 1},{deviceType: "Tablet", deviceCount: 10},{deviceType: "Notebook", deviceCount: 30},{deviceType: "Desktop", deviceCount: 20}];
const result = Array.from( responseData.reduce( (acc, o) => (acc.get(o.deviceType).deviceCount += o.deviceCount, acc), new Map(responseData.map( ({deviceType}) => [deviceType, {deviceType, deviceCount: 0} ] )) ).values());
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ruby - Group an array of hashes by key
Here is my try
#!/usr/bin/env ruby
data = [
{"name"=> "Amy", "win" => 1, "defeat" => 0},
{"name"=> "Amy", "win" => 1, "defeat" => 3},
{"name"=> "Carl", "win" => 0, "defeat" => 1},
{"name"=> "Carl", "win" => 2, "defeat" => 1}
]
merged_hash = data.group_by { |h| h['name'] }.map do |_,val|
val.inject do |h1,h2|
h1.merge(h2) do |k,o,n|
k == 'name' ? o : o + n
end
end
end
merged_hash
# => [{"name"=>"Amy", "win"=>2, "defeat"=>3},
# {"name"=>"Carl", "win"=>2, "defeat"=>2}]
Answer to the edited post :-
#!/usr/bin/env ruby
data = [
{id: 1, name: "Amy", win: 1, defeat: 0},
{id: 1, name: "Amy", win: 1, defeat: 3},
{id: 2, name: "Carl", win: 0, defeat: 1},
{id: 2, name: "Carl", win: 2, defeat: 1}
]
merged_hash = data.group_by { |h| h.values_at(:name, :id) }.map do |_,val|
val.inject do |h1,h2|
h1.merge(h2) do |k,o,n|
%i(id name).include?(k) ? o : o + n
end
end
end
merged_hash
# => [{:id=>1, :name=>"Amy", :win=>2, :defeat=>3},
# {:id=>2, :name=>"Carl", :win=>2, :defeat=>2}]
Group array of hashes on key and append nested array on another hash key
well it ain't pretty but here you go:
my_array.
group_by { |elem| elem[:first_hash_key] }.
transform_values { |vals| vals.map { |val| val[:second_hash_key] } }.
reduce([]) do |memo, (key, vals)|
memo + [{first_hash_key: key, second_hash_key: vals}]
end
returns:
[
{
:first_hash_key=>{:id=>1, :title=>"my title"},
:second_hash_key=>[
["stuff", "more stuff", "more stuff"],
["stuff 3", "uniq stuff 2"]
]
}, {
:first_hash_key=>{:id=>2, :title=>"my other title"},
:second_hash_key=>[
["interesting stuff", "uniq stuff"]
]
}
]
If you're not clear how this works you should inspect the result of each line, but to summarize
- group by
first_hash_key
call
transform_values
to get the values atsecond_hash_key
corresponding to each of thesefirst_hash_key
valuesAt this point you have a hash mapping
first_hash_key
value to all of the corresponding values ofsecond_hash_key
. Now just have toreduce
it to get the final data structure.
This pattern of group_by => transform_values => reduce is something I use all the time and is very useful. transform_values is available in rails as well as ruby 2.5 I believe.
Related Topics
How to Open Files Relative to Home Directory
Why Should I Use Rspec or Shoulda with Rails
Rails: Respond_To JSON and HTML
Rails - Testing JSON API with Functional Tests
How to Return Multiple Tags from a Rails Helper
Key Issue with Installing Rvm (Ruby Version Manager)
Installing Gem Fails with Permissions Error
"Ago" Date/Time Functions in Ruby/Rails
Testing Actionmailer Multipart Emails(Text and HTML Version) with Rspec
Openssl, Rvm, Brew, Conflicting Error
Carrierwave Crop Specific Version
Conditional Tag Wrapping in Rails/Erb
How to Call Applicationcontroller Methods from Applicationhelper
How to Do a Regex Search in Nokogiri for Text That Matches a Certain Beginning