How to merge two hashes that have same keys in ruby - ruby

I have a two hashes that should have same keys like:
a = {a: 1, b: 2, c: 3}
b = {a: 2, b: 3, c: 4}
And I want to sum up each values like this:
if a.keys == b.keys
a.values.zip(b.values).map{|a, b| a+b}
end
But this code doesn't work if the order of keys are different like b = {a: 2, c: 4, b: 3}.
How can I write the code taking into account about order of keys?

Use Hash#merge or Hash#merge!:
a = {a: 1, b: 2, c: 3}
b = {a: 2, c: 4, b: 3}
a.merge!(b) { |k, o, n| o + n }
a # => {:a=>3, :b=>5, :c=>7}
The block is called with key, old value, new value. And the return value of the block is used as a new value.

If you're using Active Support (Rails), which adds Hash#transform_values, I really like this easy-to-read solution when you have n hashes:
hashes = [hash_1, hash_2, hash_3] # any number of hashes
hashes.flat_map(&:to_a).group_by(&:first).transform_values { |x| x.sum(&:last) }

Related

Ruby: returning a hash of key value pairs up to a given length of key value pairs

Given a hash of arbitrary length, how do i return only a number of key value pairs (as a new hash) up to a certain point given as an integer?
For example:
hash = {a: 6, b: 2, c: 1, d: 5, e: 3, f: 4}
desiredlength = 3
desiredoutput = hash = {a: 6, b: 2, c: 1}
(In my example I deliberately gave the hash values as random numbers, since I'd like a method that does not rely on the content of the values like .sort, just the order in which they appear in the hash)
This seems like it should be simple but I haven't found a good method yet.
I did come up with this method:
Hash[hash.to_a[0, desiredlength]]
This takes the original hash, turns it into an array, and converts the range of 0 to desiredlength back into a hash
... but it feels super clunky and rubocop doesn't like it
As with many things in ruby, There's More Than One Way To Do It.
Here's one:
hash = {a: 6, b: 2, c: 1, d: 5, e: 3, f: 4}
hash.first(3).to_h
#=> {:a=>6, :b=>2, :c=>1}
# Or, similarly:
Hash[hash.first(3)]
See: Enumberable#first.
hash = {a: 6, b: 2, c: 1, d: 5, e: 3, f: 4}
desiredlength = 3
hash.select { (desiredlength -= 1) >= 0 }
#=> {a: 6, b: 2, c: 1 }
See Hash#select.
Another way:
hash.slice(*hash.keys.first(desiredlength))
#=> {a: 6, b: 2, c: 1 }
See Hash#slice.
Here is another way it can be done:
hash = {a: 6, b: 2, c: 1, d: 5, e: 3, f: 4}
desiredlength = 3
hash.take(desiredlength).to_h

Join an array with a block natively

Is there a native way to join all elements of an array into a unique element like so:
[
{a: "a"},
{b: "b"}
].join do | x, y |
x.merge(y)
end
To output something like:
{
a: "a",
b: "b"
}
The fact that I used hashes into my array is an example, I could say:
[
0,
1,
2,
3
].join do | x, y |
x + y
end
Ends up with 6 as a value.
Enumerable#inject covers both of these cases:
a = [{a: "a"}, {b: "b"}]
a.inject(:merge) #=> {:a=>"a", :b=>"b"}
b = [0, 1, 2, 3]
b.inject(:+) #=> 6
inject "sums" an array using the provided method. In the first case, the "addition" of the sum and the current element is done by merging, and in the second case, through addition.
If the array is empty, inject returns nil. To make it return something else, specify an initial value (thanks #Hellfar):
[].inject(0, :+) #=> 0
[
{a: "a"},
{b: "b"}
].inject({}){|sum, e| sum.merge e}

How to multiply values of same keys in hashes

Given two hashes hash1 and hash2, which may be of different size, I need to multiply the values of any recurrence of a key; the extra key-value pairs should not be taken into account.
Consider for instance the example below:
hash1 = { a: 2, b: 3, c: 4 }
hash2 = { a: 3, b: 4 }
Common keys are :a and :b (:c should not be considered). What can I do to select only :a and :b and return 2 * 3 (for :a) and 3 * 4 (for :b), that is array [6, 12]?
hash1.map { |k, v| v * hash2[k] if hash2.key? k }.compact
common_keys = hash1.keys & hash2.keys
multiply = []
common_keys.each do |key|
multiply << hash1[key] * hash2[key]
end
puts multiply

Get single hash value inside array of hashes

I have an array of hashes:
array = [
{
a: 1,
b: 2,
c: [3]
},
{
a: 1,
b: 2,
c: [3, 4, 5]
},
]
and would like to target the value 3 inside the array value for the c: key in the first hash. I assume something would be added to array[0] to capture that specific value.
In the Hash, c: [3] is syntax sugar for :c => [3]. So, to access the value 3:
array[0][:c][0]
#=> 3

How do I create a custom "merge" method for hashes?

How do I implement a "custom_merge" method?
h1 = {a: 1, c: 2}
h2 = {a: 3, b: 5}
This is a standard "merge" method implementation:
h1.merge(h2) # => {:a=>3, :c=>2, :b=>5}
My desired "custom_merge" method should implement:
h1.custom_merge(h2) # {a: [1, 3], b: 5, c: 2}
No need the custom_merge method. Ruby core supplied Hash#merge with block will help you out.
h1 = {a: 1, c: 2}
h2 = {a: 3, b: 5}
h3 = h1.merge(h2){|k,o,n| [o,n]}
h3
# => {:a=>[1, 3], :c=>2, :b=>5}
class Hash
def custom_merge other
merge(other){|_, *a| a}
end
end

Resources