Ruby array of hash. group_by and modify in one line
array.group_by{|h| h[:type]}.each{|_, v| v.replace(v.map{|h| h[:name]})}
# => {"Meat"=>["one", "two"], "Fruit"=>["four"]}
Following steenslag's suggestion:
array.group_by{|h| h[:type]}.each{|_, v| v.map!{|h| h[:name]}}
# => {"Meat"=>["one", "two"], "Fruit"=>["four"]}
Ruby - array of hashes group by based on similar hash key
You need to group_by
and then reduce
values merging them.
input.
group_by { |h| h["PROJECT"] }.
values.
map do |v|
v.reduce({}) do |acc, h|
acc.merge(h) { |_, v1, v2| v1 == v2 ? v1 : [*v1, *v2] }
end
end
#⇒ [{"PROJECT"=>"awesome_project1",
# "VERSION"=>[128, 64],
# "STATUS"=>["not required", "scheduled"]},
# {"PROJECT"=>"awesome_project2",
# "VERSION"=>32,
# "STATUS"=>"finished"}]
Ruby hashes group by multiple value
This seems to do what you want:
transformed_hash = initial_hash
.group_by { |x| x['name'] }
.map { |k, v| [k, v.group_by{ |x| x['folder'] }] }
.to_h
Group array of hashes by multiple keys
I would do it this way:
def group_hashes arr, group_fields
arr.group_by {|hash| hash.values_at(*group_fields).join ":" }.values.map do |grouped|
grouped.inject do |merged, n|
merged.merge(n) do |key, v1, v2|
group_fields.include?(key) ? v1 : (v1.to_f + v2.to_f).to_s
end
end
end
end
You can group by multiple keys by joining them with some delimiter, see arr.group_by {|hash| hash.values_at(*group_fields).join ":" }
And I suppose, you don't need sum_field
, because generally all fields that aren't grouped should be summed up.
Ruby group hashes based on matching keys and store the value of non matching keys in an array
You can try grouping by key
and value
and then map the revenue
values:
foos
.group_by { |e| e.values_at(:key, :value) }
.map do |(key, value), values|
{ key: key, value: value, revenue: values.map { |e| e[:revenue] } }
end
# [{:key=>"Foo", :value=>1, :revenue=>[2, 4]}, {:key=>"Bar", :value=>2, :revenue=>[7]}, {:key=>"bar", :value=>2, :revenue=>[9]}, {:key=>"Zampa", :value=>4, :revenue=>[9]}]
i want to make a group Hash by keys and add values
I'd use the each_with_object
method.(key, value)
here is a deconstruction of each key/value pair like 93=>1
, hash
is an intermediate object to store the result.
data.each_with_object({}) do |(key, value), hash|
result_key =
case key
when 10..19 then 1
when 20..29 then 2
when 90..99 then 9
end
next if result_key.nil?
hash[result_key] ||= 0
hash[result_key] += value
end
For the provided input I got {9=>2, 2=>7856, 1=>267}
UPD
A shorter solution suggested by Holger Just and Stefan in the comments below.
data.each_with_object(Hash.new(0)) do |(key, value), hash|
hash[key / 10] += value
end
With Hash.new(0)
the initial object will be a hash with the default value 0
> hash = Hash.new(0)
=> {}
> hash[1]
=> 0
Ruby multiple group_by of array
Quick answer, using your code above:
y = [{:a=>"2017-01-01", :b=>"2", :c=>"1"}, {:a=>"2017-01-01", :b=>"2", :c=>"2"}, {:a=>"2017-01-02", :b=>"5", :c=>"1"}]
p = y.group_by { |g| g[:a]}
q = p.map {|date, list| [date, list.group_by {|g| g[:b]}]}.to_h
This gives the desired result of:
q == {
"2017-01-01" => {
"2"=> [
{:a=>"2017-01-01", :b=>"2", :c=>"1"},
{:a=>"2017-01-01", :b=>"2", :c=>"2"}
]
},
"2017-01-02" => {
"5"=> [
{:a=>"2017-01-02", :b=>"5", :c=>"1"}
]
}
}
This slightly odd pattern of mapping the hash to a list of arrays, then converting it back to a hash (using to_h
) can be simplified slightly if you are working with a Rails v4.2+ project, by use of Hash#transform_values
:
p.transform_values {|list| list.group_by {|g| g[:b]}}
However with that said, it is generally considered bad practice to be manipulating complex, nested hash objects like this. If your code gets too complicated, you may wish to consider redesigning how it works, in a more Object-Oriented manner.
Using group_by but return an array of hashes
The reason the grouped values are Rails objects (your models) is due to the fact that you also start with these objects. You can use the attributes
method to retrieve the attributes of a model instance as a hash.
The following achieves the result you want:
City.all.group_by { |city| city.state.name }
.transform_values { |cities| cities.map(&:attributes) }
If you only want specific attributes, use slice
instead:
City.all.group_by { |city| city.state.name }
.transform_values { |cities| cities.map { |city| city.slice(:id, :name) } }
Note that slice
will return an ActiveSupport::HashWithIndifferentAccess
instance. Which mostly can be used in the same manner as a normal hash, but returns the same value for both hash[:name]
and hash['name']
. If you rather use a normal hash append a to_hash
call after the slice
call.
Related Topics
Deleting While Iterating in Ruby
Counting Days Excluding Weekends
Most Efficient Way to Calculate Hamming Distance in Ruby
How to Get the Current Route in Rails
How to Run Phantomjs on Heroku
Devise Skip_Confirmation! Fails to Avoid to Send the Confirmation Instructions
Rvm Install: Ruby Installation Error
Trouble Resizing the Default Image with Paperclip
How to Use Ruby Metaprogramming to Add Callbacks to a Rails Model
How to Get a HTML Table Row with Capybara
Scientific Programming with Ruby
In Ruby, How to I Control the Order in Which Test::Unit Tests Are Run
What Is a Robust Installation Process for Nokogiri (On Ubuntu)
How to Configure Mongomapper and Activerecord in Same Ruby Rails Project
What's the Fastest Way to Check If a Word from One String Is in Another String