Rails 3.如何获得两个数组之间的差异?

时间:2011-12-26 23:24:48

标签: ruby-on-rails ruby arrays

假设我有这个带有货件ID的数组。

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

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

装运ID为

的发票数组
i = Invoice.find(:all, :select => "id, shipment_id")

[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>]
  • 发票属于发货。
  • 货件有一张发票。
  • 因此,发票表格中有一列shipment_id

要创建发票,我点击新发票,然后有一个包含发货的选择菜单,因此我可以选择“我为哪个发货创建发票”。所以我只想显示尚未为其创建发票的货件清单。

所以我需要一组没有发票的货件。在上面的例子中,答案是1,4,5。

9 个答案:

答案 0 :(得分:112)

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

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

答案 1 :(得分:39)

首先,您将获得发票中显示的shipping_id列表:

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

然后从原始阵列中“拒绝”它们:

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

注意:请记住,拒绝返回一个新数组,请使用拒绝!如果你想改变原始数组

答案 2 :(得分:19)

使用替代标志

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

答案 3 :(得分:5)

此前pgquardiario的答案仅包括单向差异。如果你想要两个数组的区别(因为它们都有一个唯一的项目),那么尝试类似下面的内容。

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

答案 4 :(得分:5)

这应该在一个ActiveRecord查询中执行

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

它输出SQL

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

Rails 4 + 中,您可以执行以下操作

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

它输出SQL

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

而不是select(:id)我推荐使用ids方法。

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

答案 5 :(得分:5)

Ruby 2.6引入了Array.difference

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

所以在这里给出的情况下:

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

似乎很好地解决了这个问题。我一直是a - b | b - a的追随者,尽管有时想起来可能很棘手。

这肯定可以解决这个问题。

答案 6 :(得分:2)

纯红宝石溶液是

(a + b) - (a & b)

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

a + b将在两个数组之间产生并集的地方
并且a & b返回交集
并且union - intersection将返回差额

答案 7 :(得分:0)

在处理字符串数组时,将差异分组在一起可能很有用。

在这种情况下,我们可以使用Array#zip将元素分组在一起,然后使用一个块来决定如何处理分组后的元素(数组)。

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"]
# ]

答案 8 :(得分:-1)

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