Rails 3. How to get the difference between two arrays?

Ruby on-RailsRubyArrays

Ruby on-Rails Problem Overview


Let’s say I have this array with shipments ids.

s = Shipment.find(:all, :select => "id")

[#<Shipment id: 1>, #<Shipment id: 2>, #<Shipment id: 3>, #<Shipment id: 4>, #<Shipment id: 5>]

Array of invoices with shipment id's

i = Invoice.find(:all, :select => "id, shipment_id")

[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>]
  • Invoices belongs to Shipment.
  • Shipment has one Invoice.
  • So the invoices table has a column of shipment_id.

To create an invoice, I click on New Invoice, then there is a select menu with Shipments, so I can choose "which shipment am i creating the invoice for". So I only want to display a list of shipments that an invoice hasn't been created for.

So I need an array of Shipments that don't have an Invoice yet. In the example above, the answer would be 1, 4, 5.

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

a = [2, 4, 6, 8]
b = [1, 2, 3, 4]

a - b | b - a # => [6, 8, 1, 3]

Solution 2 - Ruby on-Rails

First you would get a list of shipping_id's that appear in invoices:

ids = i.map{|x| x.shipment_id}

Then 'reject' them from your original array:

s.reject{|x| ids.include? x.id}

Note: remember that reject returns a new array, use reject! if you want to change the original array

Solution 3 - Ruby on-Rails

Use substitute sign

irb(main):001:0> [1, 2, 3, 2, 6, 7] - [2, 1]
=> [3, 6, 7]

Solution 4 - Ruby on-Rails

Ruby 2.6 is introducing Array.difference:

[1, 1, 2, 2, 3, 3, 4, 5 ].difference([1, 2, 4]) #=> [ 3, 3, 5 ]

So in the case given here:

Shipment.pluck(:id).difference(Invoice.pluck(:shipment_id))

Seems a nice elegant solution to the problem. I've been a keen follower of a - b | b - a, though it can be tricky to recall at times.

This certainly takes care of that.

Solution 5 - Ruby on-Rails

Pure ruby solution is

(a + b) - (a & b)

([1,2,3,4] + [1,3]) - ([1,2,3,4] & [1,3])
=> [2,4]

Where a + b will produce a union between two arrays
And a & b return intersection
And union - intersection will return difference

Solution 6 - Ruby on-Rails

The previous answer here from pgquardiario only included a one directional difference. If you want the difference from both arrays (as in they both have a unique item) then try something like the following.

def diff(x,y)
  o = x
  x = x.reject{|a| if y.include?(a); a end }
  y = y.reject{|a| if o.include?(a); a end }
  x | y
end

Solution 7 - Ruby on-Rails

This should do it in one ActiveRecord query

Shipment.where(["id NOT IN (?)", Invoice.select(:shipment_id)]).select(:id)

And it outputs the SQL

SELECT "shipments"."id" FROM "shipments"  WHERE (id NOT IN (SELECT "invoices"."shipment_id" FROM "invoices"))

In Rails 4+ you can do the following

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).select(:id)

And it outputs the SQL

SELECT "shipments"."id" FROM "shipments"  WHERE ("shipments"."id" NOT IN (SELECT DISTINCT "invoices"."shipment_id" FROM "invoices"))

And instead of select(:id) I recommend the ids method.

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).ids

Solution 8 - Ruby on-Rails

When dealing with arrays of Strings, it can be useful to keep the differences grouped together.

In which case, we can use Array#zip to group the elements together and then use a block to decide what to do with the grouped elements (Array).

a = ["One", "Two",     "Three", "Four"]
b = ["One", "Not Two", "Three", "For" ]

mismatches = []
a.zip(b) do |array| 
  mismatches << array if array.first != array.last
end

mismatches
# => [#   ["Two", "Not Two"], 
#   ["Four", "For"]
# ]

Solution 9 - Ruby on-Rails

s.select{|x| !ids.include? x.id}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionleonelView Question on Stackoverflow
Solution 1 - Ruby on-RailsKyle DecotView Answer on Stackoverflow
Solution 2 - Ruby on-RailspguardiarioView Answer on Stackoverflow
Solution 3 - Ruby on-Railsdenis.peplinView Answer on Stackoverflow
Solution 4 - Ruby on-RailsSRackView Answer on Stackoverflow
Solution 5 - Ruby on-RailszhismeView Answer on Stackoverflow
Solution 6 - Ruby on-Rails6ft DanView Answer on Stackoverflow
Solution 7 - Ruby on-Rails6ft DanView Answer on Stackoverflow
Solution 8 - Ruby on-RailspdobbView Answer on Stackoverflow
Solution 9 - Ruby on-RailsFez AbbasView Answer on Stackoverflow