Reputation: 299
I have two different arrays, each consisting of different hashes.
new_array1 = [
{:index=>4, :column=>0, :ID=>"ABC"},
{:index=>4, :column=>1, :ID=>"XYZ"},
{:index=>4, :column=>2, :ID=>"BCD-1547"}
]
new_array2 = [
{:index=>4, :column=>0, :ID=>"ABC"},
{:index=>4, :column=>1, :ID=>"IJK"},
{:index=>4, :column=>2, :ID=>"BCD-1547"}
]
I want to compare the values of the :ID
keys in new_array1
vs the values in new_array2
only if the :index
and :column
values are the same.
ie)
if (new_array1[0][:index] == new_array2[0][:index]) and (new_array1[0][:column] == new_array2[0][:column])
if(new_array1[0][:ID] == new_array2[0][:ID])
# do something
end
end
Is there a way to loop through all the hashes in both arrays and find the match? Or perhaps a more elegant way to do this in ruby?
Upvotes: 2
Views: 1379
Reputation: 19938
This will return an array of matching hashes:
res = new_array1.inject([]) { |memo, hash| memo << hash if new_array2.any? { |hash2| hash[:ID] == hash2[:ID] && hash[:index] == hash2[:index] && hash[:column] == hash2[:column] }; memo }
# => [{:index=>4, :column=>0, :ID=>"ABC"}, {:index=>4, :column=>1, :ID=>"XYZ"}, {:index=>4, :column=>2, :ID=>"BCD-1547"}]
res.each do |hash|
# do something
end
If an item in new_array1
has the same index
, column
and ID
keys as any item in new_array2
it will be included.
You could also simpify if these are the only keys in the hashes by using ==
to compare equality:
res = new_array1.inject([]) { |memo, hash| memo << hash if new_array2.any? { |hash2| hash == hash2 }; memo }
The inject
method, aliased and also known as reduce
, takes a collection and creates a new value from it, each time the block given to inject
is called it is given the next element of the collection and the return value of the previous block (the first time the block is called it is given the seed value passed to inject
). This allows you to build up a value similar to recursion.
There are some examples of inject
here: Need a simple explanation of the inject method
The any?
method will return true as soon as the given block returns true for any of the given collection elements. If the block never returns true then any?
returns false. So:
[0,0,0,1,0].any? { |num| num == 1 } # => true
[0,0,0,0,0].any? { |num| num == 1 } # => false
Upvotes: 2
Reputation: 110675
If all hashes have the same three keys and no other keys it's simply
new_array1 & new_array2
#=> [{:index=>4, :column=>0, :ID=>"ABC"},
# {:index=>4, :column=>2, :ID=>"BCD-1547"}]
If the hashes may have other keys as well, you can write the following.
new_array1 = [{:index=>4, :column=>0, :ID=>"ABC", :pet=>"cat"},
{:index=>4, :column=>1, :ID=>"XYZ", :bet=>"red"},
{:index=>4, :column=>2, :ID=>"BCD-1547", :met=>"Betty"}]
new_array2 = [{:index=>4, :column=>0, :ID=>"ABC", :tree=>"maple"},
{:index=>4, :column=>1, :ID=>"IJK", :colour=>"blue"},
{:index=>4, :column=>2, :ID=>"BCD-1547", :car=>"beemer"}]
keys = [:index,:column,:ID]
h1 = new_array1.each_with_object({}) { |g,h| h[g.select { |k,_| keys.include? k }] = g }
#=> {{:index=>4, :column=>0, :ID=>"ABC"}=>
# {:index=>4, :column=>0, :ID=>"ABC", :pet=>"cat"},
# {:index=>4, :column=>1, :ID=>"XYZ"}=>
# {:index=>4, :column=>1, :ID=>"XYZ", :bet=>"red"},
# {:index=>4, :column=>2, :ID=>"BCD-1547"}=>
# {:index=>4, :column=>2, :ID=>"BCD-1547", :met=>"Betty"}}
h2 = new_array2.each_with_object({}) { |g,h| h[g.select { |k,_| keys.include? k }] = g }
#=> {{:index=>4, :column=>0, :ID=>"ABC"}=>
# {:index=>4, :column=>0, :ID=>"ABC", :tree=>"maple"},
# {:index=>4, :column=>1, :ID=>"IJK"}=>
# {:index=>4, :column=>1, :ID=>"IJK", :colour=>"blue"},
# {:index=>4, :column=>2, :ID=>"BCD-1547"}=>
# {:index=>4, :column=>2, :ID=>"BCD-1547", :car=>"beemer"}}
(h1.keys & h2.keys).map { |k| [h1[k], h2[k]] }
#=> [[{:index=>4, :column=>0, :ID=>"ABC", :pet=>"cat"},
# {:index=>4, :column=>0, :ID=>"ABC", :tree=>"maple"}],
# [{:index=>4, :column=>2, :ID=>"BCD-1547", :met=>"Betty"},
# {:index=>4, :column=>2, :ID=>"BCD-1547", :car=>"beemer"}]]
Upvotes: 0
Reputation: 121000
[new_array1, new_array2].map do |a|
a.group_by { |e| [e[:index], e[:column]] }
end.reduce do |f, l|
f.merge(l) { |_, f, l| [f.first[:ID], l.first[:ID]] }
end
# => {
# [ 4, 0 ] => [
# [0] "ABC",
# [1] "ABC"
# ],
# [ 4, 1 ] => [
# [0] "XYZ",
# [1] "IJK"
# ],
# [ 4, 2 ] => [
# [0] "BCD-1547",
# [1] "BCD-1547"
# ]
# }
Put do_something unless f.first[:ID] == l.first[:ID]
instead of [f.first[:ID], l.first[:ID]]
in the last clause to do whatever you want.
Upvotes: 0