I got two hashes hash_a and hash_b which are actually arrays but have hash inside them. Those hash have unique key.
hash_a = [
{:unique_key => 1, :data => 'data for A1'},
{:unique_key => 2, :data => 'data for A2'},
{:unique_key => 3, :data => 'data for A3'}
]
hash_b = [
{:unique_key => 1, :data => 'data for B1'},
{:unique_key => 2, :data => 'data for B2'},
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
Now I want to find out difference between hash_a and hash_b, such that I get the hash_c as array of new hashes present in hash_b. I basically want hash_b - hash_a
So I want this output for hash_c, hash_c should be this:
[
{:unique_key => 1, :data => 'data for A1'},
{:unique_key => 2, :data => 'data for A2'},
{:unique_key => 3, :data => 'data for A3'},
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
I have tried something like this:
hash_c = hash_a
hash_b.each do |inner_bhash|
found = 0
hash_a.each do |inner_ahash|
if(inner_ahash[:unique_key] == inner_bhash[:unique_key])
found = 1
break
end
end
if(found==0)
hash_c.push(inner_bhash)
end
end
This is doing the trick, but I want a better way. Like hashmap or something, I don't know what.
In addition, I may want to see only the new entries, ie
[
{:unique_key => 4, :data => 'data for B4'},
{:unique_key => 5, :data => 'data for B5'}
]
I can do that in my code by replacing
hash_c = hash_a
with
hash_c = []
but how could I adapt this requirement in the same way?
With Hash
es you can use merge
to do what you want - so going through making each Array
into a Hash
you can do the following:
hash_b.group_by { |e| e[:unique_key] }.
merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten
# => [{:unique_key=>1, :data=>"data for A1"},
# {:unique_key=>2, :data=>"data for A2"},
# {:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"},
# {:unique_key=>3, :data=>"data for A3"}]
If you want to have just the entries of hash_b
(which do not have a key in hash_a
), given you already have the solution above - you can simply subtract hash_a
from the result:
hash_b.group_by { |e| e[:unique_key] }.
merge(hash_a.group_by { |e| e[:unique_key] }).values.flatten - hash_a
# => [{:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
Another, more straight forward way is to filter out all elements of hash_b
who have an entry in hash_a
:
hash_b.select { |x| hash_a.none? { |y| x[:unique_key] == y[:unique_key] } }
# => [{:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
You can use the form of Array#uniq that takes a block.
(hash_a + hash_b).uniq { |h| h[:unique_key] }
#=> [{:unique_key=>1, :data=>"data for A1"}, {:unique_key=>2, :data=>"data for A2"},
# {:unique_key=>3, :data=>"data for A3"}, {:unique_key=>4, :data=>"data for B4"},
# {:unique_key=>5, :data=>"data for B5"}]
To quote the doc, "self is traversed in order, and the first occurrence is kept."
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.