Reputation: 3196
Assuming I got a hash of old properties from an object:
old = {a: 1, b: 2, c: nil}
Then I got a hash of new properties from user input (all keys must exists in old hash):
new = {a: 2}
Finally I want get a hash of properties to update to the object like this:
upd = {a: 2, b: nil, c: nil}
# upd = {a: 2, b: nil} would be better
Now I use map
to implement this:
upd = Hash[old.map{|x| [ x[0],new[x[0]] ] } ]
I tried merge
but couldn't get what I want:
old.merge(new){|k,old,new| new} #=> {:a=>2, :b=>2, :c=>nil}
But I think there must be some better way of doing this. Any help will be appreciated.
Upvotes: 0
Views: 1864
Reputation: 110725
This is one way:
old.merge(old) { |k,_,_| new[k] }
#=> {a: 2, b: nil, c: nil}
This uses the form of the method Hash#merge that takes a block. The purpose of the block is to resolve values for keys that are present in both hashes involved in the merge.
Here I am merging old
with itself, so the value for every key in old
is determined by the block.
An array [key, old_value, new_value]
is passed to the block. In this case old_value
and new_value
are the same, but as I will not be using that value in the block, I have replaced the two corresponding block variables with the placeholder _
.
In the block I compute the value for key k
to be new[k]
, which is nil
if new
does not have a key k
.
This approach could also be implemented with Enumerable#reduce (a.k.a inject
):
old.reduce(old) { |h,(k,_)| h[k] = new[k]; h }
Upvotes: 2
Reputation: 18587
old.inject(new.dup) { |h, (k,v)| h[k] = nil unless (v.nil? || new.has_key?(k)); h }
# => {a: 2, b: nil}
Basically, it says to start with a copy of new
. Then build up the resulting hash h
by iterating over the (k,v)
pairs in old
.
The only extra things we want in addition to what's in new
is when there was a key in old
with a non-nil value, but now the key is not present in new
and so we want to indicate that by having the key in the result, but with a nil
value. So if (k,v)
is a par in old
, and h
is the result we're building, then we want to add h[k] = nil
if v
was not nil, but k
wasn't even a key in new
. Since there's a bunch of negations in there, it's simpler to express it with an unless
, resulting in the form above.
Upvotes: 1
Reputation: 40566
I don't think there's an out-of-the-box way to do what you want, so your current solution seems OK.
You could avoid the [0]
, by doing either:
upd = Hash[old.map{|k, v| [k, new[k]] }]
or
upd = Hash[old.keys.map{|k| [k, new[k]] }]
One caveat eith the current solution: if a key exists in new
, but does not exist in old
, it will not be present in upd
. You may be aware of this, but I thought it should be pointed out.
Upvotes: 2