sparkle
sparkle

Reputation: 7398

Intersection between array of hashes but returning the full hash

There are two arrays of hashes

actual = [{id: 1, text: "A", type: "X", state: "enabled"}, {id: 2, text: "B", type: "X", state: "enabled"}]

expected = [{text: "A", type: "X", state: "enabled"}]

I need to get the :id of all Hash not included in "expected". The comparison has to be done using three keys (text, type, state). In this case

results = [{id: 2}]

At the moment I am using this but it's very long and not performing for a big array. Is there a better way?

 actuals        = actuals.map{|a| a.slice(:text, :type, :state)}
 expected       = expected.map{|a| a.slice(:text, :type, :state)}
 not_expected   = actuals - expected
      
      results = actuals.select{|actual| 
         not_expected.find{|n| 
            n[:text]   == actual[:text] &&
            n[:type]   == actual[:type] &&
            n[:state]  == actual[:state]
         }.present?
       } 

Upvotes: 3

Views: 250

Answers (2)

Cary Swoveland
Cary Swoveland

Reputation: 110725

exp = expected.first
  #=> {text: "A", type: "X", state: "enabled"}

actual.reject { |h| h == h.merge(exp) }.map { |h| h.slice(:id) }
  #=> [{:id=>2}]

Try it

Hashes in actual are rejected if they are unaffected when merged with exp, meaning that the hashes being merged have the same values for all the keys in exp. Each remaining hash h in actual is then mapped to { id: h[:id] }, using Hash#slice.

One advantage of this approach is that the code need not be changed if exp is changed to a hash having different keys.

Upvotes: 3

Chiara Ani
Chiara Ani

Reputation: 1093

actual = [{id: 1, text: "A", type: "X", state: "enabled"}, {id: 2, text: "B", type: "X", state: "enabled"}]
expected = [{text: "A", type: "X", state: "enabled"}]
comparable_expected = expected.map { |e| e.slice(:text, :type, :state) }

results = actual.select do |a| 
  not comparable_expected.include? a.slice(:text, :type, :state)
end

resulting_ids = results.map(&:id)

Upvotes: 5

Related Questions