Rails Mapping Array of Hashes Onto Single Hash

Rails mapping array of hashes onto single hash

You could compose Enumerable#reduce and Hash#merge to accomplish what you want.

input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Reducing an array sort of like sticking a method call between each element of it.

For example [1, 2, 3].reduce(0, :+) is like saying 0 + 1 + 2 + 3 and gives 6.

In our case we do something similar, but with the merge function, which merges two hashes.

[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
is {:a => 1, :b => 2, :c => 3}

Convert array of hashes to single hash with values as keys

This should do what you want

countries.each_with_object({}) { |country, h| h[country[:country].to_sym] = country[:cost] }
=> {:england=>12.34, :scotland=>56.78}

How can I convert a ruby array of hashes into a single hash?

arr = [{"name"=>"apple",  "value"=>"red"},
{"name"=>"banana", "value"=>"yellow"},
{"name"=>"grape", "value"=>"purple"}]

Hash[arr.map { |h| [h["name"].to_sym , h["value"]] }]
#=> {:apple=>"red", :banana=>"yellow", :grape=>"purple"}

With Ruby 2.1+

arr.map { |h| [h["name"].to_sym , h["value"]] }.to_h
#=> {:apple=>"red", :banana=>"yellow", :grape=>"purple"}

How to convert array of hashes into a single hash and count repeated items?

You can merge to v (within the map call) the value of count (v.merge(:count => v.length)), so this will add the count key to the v hash, you'll get something like:

[
{"AVOCADO"=>{:price=>3.0, :clearance=>true, :count=>2},
{"KALE"=>{:price=>3.0, :clearance=>false, :count=>2}
]

But anyways the values for :count are going to be wrong.

In the other hand, you can get all the keys from each hash in cart_items, merge the hashes, and then merge a new key with the count of that key in the stored keys array:

def consolidate_cart(items)
items_keys = items.flat_map(&:keys)
items.inject(:merge).map do |key, value|
{ key => value.merge(count: items_keys.count(key)) }
end
end

p consolidate_cart(cart_items)
# [{"AVOCADO"=>{:price=>3.0, :clearance=>true, :count=>2}}, {"KALE"=>{:price=>3.0, :clearance=>false, :count=>1}}]

A part by part view of the method functioning:

You map the keys of each hash item (items.flat_map(&:keys)):

["AVOCADO", "AVOCADO", "KALE"]

You merge the hash within items (items.inject(:merge)):

{"AVOCADO"=>{:price=>3.0, :clearance=>true}, "KALE"=>{:price=>3.0, :clearance=>false}}

When you iterate over the previous generated hash, you merge to each hash value the count key ({ key => value.merge(count: items_keys.count(key)) }):

# {:price=>3.0, :clearance=>true}
# {:count=>2}
# => {:price=>3.0, :clearance=>true, :count => 2}

I've already seen my answer doesn't correspond with the expected output. This does:

def consolidate_cart(items)
items.inject(:merge).each_with_object(items: items.flat_map(&:keys)) do |(k, v), hash|
hash[k] = v.merge(count: hash[:items].count(k))
end.reject { |k, _| k == :items }
end

How to create an array of hashes by mapping 2 hashes in ruby on rails

You can use a regular Array#map for this:

first_array_of_hash = [{:name => "John", :age => 34, :nullable => 'yes'},{:name => "Rose", :age => 30, :nullable => 'no'}]

second_hash = {:field_name => "", :field_age => nil, :field_nullable => false, :field_default => ""}

def transform(object)
{
field_name: object[:name],
field_age: object[:age],
field_nullable: object[:mode] == 'nullable'
}
end

result = first_array_of_hash.map do |object|
second_hash.merge(transform(object))
end

puts result

How to separate an array of hashes into different arrays if a key is equal?

Given:

tst=[
{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>5.0},
{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.84},
{"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>0.0},
{"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.4}
]

You can use .group_by to get a hash of elements by key. In this case, use the key ["beneficiary_document"] passed to the block and you will get a hash of arrays by that key -- two in this case.

You can do:

tst.group_by { |h| h["beneficiary_document"] }
# {"43991028"=>[{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>5.0}, {"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>0.0}], "71730550"=>[{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.84}, {"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.4}]}

To see it pretty printed:

require "pp"
PP.pp(tst.group_by {|h| h["beneficiary_document"] },$>,120)
{"43991028"=>
[{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>5.0},
{"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"43991028", "calification_by_qualifier"=>0.0}],
"71730550"=>
[{"user_id"=>2, "user_name"=>"Pepo", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.84},
{"user_id"=>3, "user_name"=>"Carlos", "beneficiary_document"=>"71730550", "calification_by_qualifier"=>3.4}]}

You can also achieve the same result with a hash that returns an array as a default procedure, then call .map over tst and push the hash into the array by that key:

h=Hash.new { |h,k| h[k]=[] }
tst.map { |eh| h[eh["beneficiary_document"]].push(eh) }

Or, combine that into a single statement:

tst.each_with_object(Hash.new { |h,k| h[k]=[] }) { |g,h|
h[g["beneficiary_document"]].push(g)}

All three methods create identical hashes. The first, .group_by, is the easiest.

Converting an array of hashes to ONE hash in Ruby

I think the key to the solution is Hash[], which will create a Hash based on an array of key/values, i.e.

Hash[[["key1", "value1"], ["key2", "value2"]]]
#=> {"key1" => "value1", "key2" => "value2"}

Just add a set of map, and you have a solution!

result = [
{"id_t"=>["1"], "transcript_t"=>["I am a transcript ONE"]},
{"id_t"=>["2"], "transcript_t"=>["I am a transcript TWO"]},
{"id_t"=>["3"], "transcript_t"=>["I am a transcript THREE"]}
]
Hash[result.map(&:values).map(&:flatten)]

Reducing an array of hashes into new hash

This will work:

arr.each_with_object({}) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] = hash[sym].to_i + obj[sym]
end
end

It's one iteration, you can write the nested loop as 3 different lines, but it's a bit cleaner this way in my opinion.

Note: calling to_i while getting previous value of hash[sym] as initially it is nil and nil.to_i == 0. Alternatively, you can initialize all unknown counts with 0, like this:

arr.each_with_object(Hash.new(0)) do |obj, hash|
%i[all_sales direct_sales referred_sales].each do |sym|
hash[sym] += obj[sym]
end
end


Related Topics



Leave a reply



Submit