How to merge two hashes with no new keys - ruby

How could I merge two hashes that results in no new keys, meaning the merge would merge keys that exist in both hashes?
For example, I want the following:
h = {:foo => "bar"}
j = {:foo => "baz", :extra => "value"}
puts h.merge(j) # {:foo => "baz"}
I'm looking for a really clean way of doing this as my current implementation is pretty messy.

You could remove keys that weren't in the first hash from the second hash, then merge:
h.merge j.select { |k| h.keys.include? k }
Unlike my edited-out alternative, this is safe if you decide to change it to a merge! or update.

If you're using activesupport (part of rails), you can take advantage of 2 extra methods on Hash:
Hash#slice takes the desired keys as separate arguments (not an array of keys) and returns a new hash with just the keys you asked for.
Hash#except takes the same arguments as slice, but returns a new hash with keys that were not in the arguments.
First load activesupport:
require 'active_support/core_ext'
Merge only entries from j whose keys are already in h (i.e. modify, but don't add any or remove entries in h):
h.merge(j.slice(*h.keys))
Example:
ignore_new = ->(h, j) { h.merge(j.slice(* h.keys)) }
ignore_new.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:a=>1, :b=>10, :c=>11}
Get the leftovers from j that weren't in h:
j.except(*h.keys)
Bonus:
If you want true intersection, meaning you want a result that only has keys that are in common between the 2 hashes, do this:
h.merge(j).slice(* ( h.keys & j.keys ) )
Example:
intersect = ->(h, j) { h.merge(j).slice(* (h.keys & j.keys) ) }
intersect.({a: 1, b: 2, c: 3}, {b: 10, c: 11, d: 12})
# => {:b=>10, :c=>11}
and leftovers from h that weren't in j:
h.except(*j.keys)
You may also want to use activesupport's HashWithIndifferentAccess if you want string & symbol key-access to be considered equivalent.
Note that none of the above examples change the original hashes; new hashes are returned instead.

Yjerem's answer works in Ruby 1.9, but not in 1.8.x. In 1.8.x the Hash#select method returns an array. Hash#reject returns a hash.
h.reject { |k,v| !j.keys.include? k }
If you want to keep only key-value pairs that have identical values, you can do this:
h.reject { |k,v| j[k] != h[k] }
The edge case there is nils. If you are storing nils in your Hash then you have to do this:
h.reject { |k,v| !j.has_key? k or j[k] != h[k] }

[h].inject({}) { |m,e| e.merge(j) { |k,o,n| m[k] = n }; m}
or
[{}].inject(h) { |m,e| m.merge(j) { |k,o,n| e[k] = n }; e}
or (probably the best, but not technically a single expression) ...
t = {}; h.merge(j) { |k,o,n| t[k] = n }; t

The more customized way of doing this is:
h = {"foo"=> "bar"}
j = {"foo" => "baz", "extra" => "value"}
k = h.merge(j)
result: {"foo"=>"baz", "extra"=>"value"}
Here the key "foo" in the second hash is overriding the "foo" in first hash.But if you want to keep the old value i.e bar or if you want keep the new value i.e "baz"? You can do something like this:
k = h.merge(j){|key, old, new| old}
result: {"foo"=>"bar", "extra"=>"value"}
k = h.merge(j){|key, old, new| new}
result: {"foo"=>"baz", "extra"=>"value"}

Related

Add value to the front of hash and mutate

i want to add a value to the front of a hash and i want the hash to mutate.
Like so:
def put!(q,v)
q = {:value => v, :next => q}
end
But this doesn't work because i can't assign q a new value like this.
How would i do this?
Thanks in advance for all the answers.
I'm assuming that the :next key should have the image of the hash before modification. You can do this...
def put!(q,v)
q[:value], q[:next] = v, q.dup
end
the hash refrenced as q will be mutated so other references to the hash will reflect the change.
I have no idea why you'd ever want to do this, but here's a way to prepend a key & value pair to your hash :
hash = {b: 2, c: 3}
hash_copy = hash.dup
hash.clear
hash[:a] = 1
hash_copy.each do |k,v|
hash[k] = v
end
p hash
# {:a=>1, :b=>2, :c=>3}
It's slow and kinda useless: you need to duplicate your hash, remove all the keys, add one pair, and put all the pairs back.
The usual way would be to not care about the order :
hash = {b: 2, c: 3}
hash[:a] = 1
p hash
# {:b=>2, :c=>3, :a=>1}

Is there a one liner or more efficient way to get this done?

Given a hash, for example:
hash = { "fish" => 2, "cat" => 3, "dog" => 1 }
I need to get:
A comma separated string for all the values, E.g. "2,3,1"
An integer that hold the total sum of the values, E.g. 6
My current code:
value_string = hash.map { |k,v| "#{v}"}.join(',')
sum = 0
hash.map { |k,v| sum += v}
You can do it like this:
hash.values.join(",") # => "2,3,1"
hash.values.inject(:+) # => 6
Here is a solution to compute both the values in single iteration (some sort of one liner)
r1,r2 = hash.reduce([nil, 0]){|m,(_,v)| [[m[0], v.to_s].compact.join(","),m[-1]+v]}
#=> ["2,3,1", 6]

How do I create a hash where the keys are values from an array Ruby

I have an array:
arr = [a, ab, abc]
I want to make a hash, using the values of the array as the keys:
newhash = [a[1], ab[1], abc[1]]
I have tried:
arr.each do |r|
newhash[r] == 1
end
to no avail.
How would I about accomplishing this in ruby?
If you are feeling like a one-liner, this will work as well
h = Hash[arr.collect { |v| [v, 1] } ]
collect is invoked once per element in the array, so it returns an array of 2-element arrays of key-value pairs.
Then this is fed to the hash constructor, which turns the array of pairs into a hash
You could also use the #reduce method from Enumerable (which is included into the Array class).
new_hash = arr.reduce({}) { |hsh, elem| hsh[elem] = 1; hsh }
And your new_hash looks like this in Ruby:
{"a": 1, "ab": 1, "abc": 1}
== is comparison. = is assigning. So just modify == into =. It works.
newhash = {}
arr.each do |r|
newhash[r] = 1
end
(I believe a, ab, abc are strings)
To learn more, this might help you. Array to Hash Ruby
You can do it like this:
ary = [[:foo, 1], [:bar, 2]]
Hash[ary] # => {:foo=>1, :bar=>2}
If you want to do it like you tried earlier, you want to initialize hash correctly:
ary = [:foo, :bar]
hash = {}
ary.each do |key|
hash[key] = 1
end # => {:foo=>1, :bar=>2}

Ruby - find the key(s) of the largest value(s) of a hash

I have a hash and I want to return the key(s) (or key/value pair(s)) of the max value(s) of the hash. So, if there is only one true max, it will return that one key; however, if there are multiple key/value pairs with the same value, it will return all of these keys. How can I accomplish this in Ruby?
my_hash.max_by {|k,v| v} #only returns one key/value pair
If you want all pairs, I would do something like
max = my_hash.values.max
Hash[my_hash.select { |k, v| v == max}]
A single liner:
my_hash.reduce({}){|h,(k,v)| (h[v] ||= []) << k;h}.max
irb
> z = {:tree => 3, :two => 2, 'three' => 3}
> z.reduce({}){|h,(k,v)| (h[v] ||= []) << k;h}.max
[3, [:tree, "three"]]

Sorting a Hash by the values in another hash

I would like to sort the keys of Hash2 by the order of the values of Hash1.
Hash2 does not need to contain all the values of Hash1.
Hash1 does not need to contain all the keys of Hash2.
If a key exists in Hash2 that does not have a corresponding value in Hash1, it should be ordered below any existing ordered keys.
Hash1 = {
p0: "q11",
p1: "q55",
p2: "q92",
p3: "q77"
}
Hash2 = {
q55: {...},
q23: {...},
q59: {...},
q98: {...},
q11: {...}
}
=>
DesiredHash = {
q11: {...},
q55: {...},
q23: {...},
q59: {...},
q98: {...}
}
What is the most Ruby-ish way of achieving this?
Similar to #sawa's but the use of Hash#values really cleans things up imho:
keys = Hash1.values.map &:to_sym
Hash[Hash2.sort_by{|k, v| keys.index(k) || keys.length}]
array = Hash1.map{|k, v| [k, v.to_sym]}
Hash[Hash2.sort_by{|k, _| array.index{|_, v| k == v} || array.length}]
Or, perhaps less efficient but shorter:
array = Hash1.to_a
Hash[Hash2.sort_by{|k, _| array.index{|_, v| k == v.to_s} || array.length}]
Explanation (Using the second one)
Hash1 will be used as a reference, which means you want to do something with its "index". Since hashes do not have the notion of index, you have to convert it into an array. to_a does that. Next, you want to sort Hash2 (which means it will be implicitly converted to an array with key-value pairs) in the order the index of array such that the key of Hash2 matches the "value" of array. index{...} will look up such index. In case there is no match, array.length will be assigned, which is bigger than any index of array. Finally, you want to turn that pairs of key-value back into a hash. That is done by Hash[...].
hash1 = {
p0: "q11",
p1: "q55",
p2: "q92",
p3: "q77"
}
hash2 = {
q55: 1,
q23: 2,
q59: 3,
q98: 4,
q11: 5
}
def get_key(hash, value)
hash.each { |k, v| return k if v == value }
return hash.keys.max
end
Hash[hash2.sort_by { |a, _| get_key(hash1, a.to_s) }]
#=> {:q11=>5, :q55=>1, :q23=>2, :q98=>4, :q59=>3}

Resources