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

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

Related

Ruby hash by reference

I want to have a single target and then have that target be modifiable in a hash.
target = [1,2,3]
hash = {1 => target, 2 => target}
Now, I want to be able to either a) change target and have the hash auto update or b) change the hash's 1 and have it automatically change hash's 2. Neither work:
target = [6,7,8]
target
output:
{1=>[1, 2, 3], 2=>[1, 2, 3]}
Plan b:
hash[1] = [6,7,8]
output:
{1=>[6, 7, 8], 2=>[1, 2, 3]}
So I'm discerning that when you make a subhash an rvalue in a hash in Ruby, it's duping the subhash before setting the lvalue to equal it. What I want is the for Ruby to not to do that. Is this possible?
Thanks,
Kevin
target is just a name, [1,2,3] is the object it is referring to. Arrays are mutable, so you can change them:
target = [1,2,3]
p hash = {1 => target, 2 => target} # =>{1=>[1, 2, 3], 2=>[1, 2, 3]}
target.replace([6,7,8])
p hash # =>{1=>[6, 7, 8], 2=>[6, 7, 8]}
You should change the array itself, as already mentioned by #steenslag.
Here is just another example which also shows it wotking on a value of the Hash. You can try other methods from the Array class.
target = [1,2,3]
h = {a: target, b: target}
h #=> {:a=>[1, 2, 3], :b=>[1, 2, 3]}
[3,4,5].each do |e|
target.shift
target.push(e)
end
h #=> {:a=>[3, 4, 5], :b=>[3, 4, 5]}
[7,8,9].each do |e|
h[:a].shift
h[:a].push(e)
end
h #=> {:a=>[7, 8, 9], :b=>[7, 8, 9]}

Ruby - How to use Hash#to_proc?

The documentation is empty, but I want to know how to use it to know when it can be useful.
that is so you can pass a hash proc into something like map.
https://bugs.ruby-lang.org/issues/11653
my_hash = ->key{{
a: 1, b: 2, c: 3, d: 4, e: 5, f: 6
}[key]}
my_hash[:a]
# => 1
[:e, :a, :b, :f, :c, :d].map(&my_hash) # hash is now mappable
# => [5, 1, 2, 6, 3, 4]

How to merge two hashes that have same keys in 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) }

Get key/value pairs as arrays

I've been struggling with this: Complete the keysAndValues function so that it takes in an object and returns the keys and values as separate arrays.
I've added several versions so you can see how i'm thinking through the problem.
def keysAndValues(data)
data.each do |data|
data.split(key.to_a, value.to_a)
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.split([key], [value])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.slice([key], [value])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each do |data|
data.slice_to.a(2)([ :a ], [ ' ' ])
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
data.each.slice_to.a(2) { |x, y| [x], [y] }
end
data
end
keysAndValues ({a: 1, b: 2, c: 3})
def keysAndValues(data)
[data.keys, data.values]
end
keys, values = keysAndValues({a: 1, b: 2, c: 3})
keys
# => [:a, :b, :c]
values
# => [1, 2, 3]
Please note that keysAndValues is not following the Ruby naming convention. The correct name should be keys_and_values.
There are built-in methods for that:
irb(main):001:0> {a: 1, b: 2, c: 3}.keys
=> [:a, :b, :c]
irb(main):002:0> {a: 1, b: 2, c: 3}.values
=> [1, 2, 3]
Or, maybe you want this? (Sorry, your question is not clear to me)
irb(main):003:0> Array({a: 1, b: 2, c: 3})
=> [[:a, 1], [:b, 2], [:c, 3]]
def keys_and_values(data)
return data.keys, data.values
end
thus:
data_hash = {a: 1, b: 2, c: 3, d: 4}
keys_and_values(data_hash)
returns:
[[a:, b:, c:, d:], [1, 2, 3, 4]]

Ruby. "Magic method" for array of hashes

Community, how to implement method "m_met" which will reorganize array of hashes (with equal size and with equal "keys") to a form where 1st element will be an array of "keys" and rest - "values" of each hash.
Example:
m_met([{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}])
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]
Thanks!
Without additional assumptions it could be something like:
hs = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
ks = hs.first.keys
[ks] + hs.map{ |h| h.values_at(*ks)}
UPDATE Frankly I would prefer second line as (so I don't need equal whatever)
ks = hs.flat_map(&:keys).uniq
h = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
h.flat_map { |j| [j.keys, j.values] }.uniq
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]
UPDATE
h = [{a: 1, b: 2, c: 3 }, {a: 1, b: 2, c: 3}]
[h.first.keys].concat(h.map(&:values))
# => [[:a, :b, :c], [1, 2, 3], [1, 2, 3]]
h = [{a: 1, b: 2, c: 3 }, {a: 4, b: 5, c: 6}]
[h.first.keys].concat(h.map(&:values))
# => [[:a, :b, :c], [1, 2, 3], [4, 5, 6]]

Resources