How to Group This Array of Hashes

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

  1. group by first_hash_key
  2. call transform_values to get the values at second_hash_key corresponding to each of these first_hash_key values

  3. At this point you have a hash mapping first_hash_key value to all of the corresponding values of second_hash_key. Now just have to reduce 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



Leave a reply



Submit