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

leo*_*nel 48 ruby arrays ruby-on-rails

假设我有这个带有出货ID的数组.

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

[#<Shipment id: 1>, #<Shipment id: 2>, #<Shipment id: 3>, #<Shipment id: 4>, #<Shipment id: 5>]
Run Code Online (Sandbox Code Playgroud)

带有货件ID的发票数组

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

[#<Invoice id: 98, shipment_id: 2>, #<Invoice id: 99, shipment_id: 3>]
Run Code Online (Sandbox Code Playgroud)
  • 发票属于Shipment.
  • 装运有一个发票.
  • 因此,发票表有一列shipment_id.

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

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

Kyl*_*cot 123

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

a - b | b - a # => [6, 8, 1, 3]
Run Code Online (Sandbox Code Playgroud)

  • 这是最优雅的答案.`a - b`返回不在b中的任何元素.`b - a`返回b中不在a和|中的任何元素 从这2个结果中返回唯一的元素集. (15认同)
  • 尽管如此,这是一种好方法,不适用于重复值。例如,如果包含到两个4的第二个4不会出现 (2认同)

pgu*_*rio 40

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

ids = i.map{|x| x.shipment_id}
Run Code Online (Sandbox Code Playgroud)

然后从原始数组中"拒绝"它们:

s.reject{|x| ids.include? x.id}
Run Code Online (Sandbox Code Playgroud)

注意:记住拒绝返回一个新数组,使用拒绝!如果要更改原始数组

  • 这比只做`xi`要快得多.数组越大,获得的速度越慢.这是我写的比较两种方法的基准http://runnable.com/U5Y8g_nsUQokbzNl/benchmark-ruby-array-diff-methods (5认同)

den*_*lin 21

使用替代标志

irb(main):001:0> [1, 2, 3, 2, 6, 7] - [2, 1]
=> [3, 6, 7]
Run Code Online (Sandbox Code Playgroud)

  • @Trip回答你的问题,你可以做这样的事情......`(ab)+(ba)`你得到两个数组中的唯一值,然后将这些值组合成一个数组. (11认同)
  • 如果你翻转这两个阵列,这将无法工作. (4认同)
  • 这个:`[2,1] - [1,2,3,2,6,7]`返回`[]`.所以它让我很好奇你如何能够从两个动态数组中获得差异,无论它们的顺序如何. (3认同)

SRa*_*ack 7

Ruby 2.6引入了Array.difference

[1, 1, 2, 2, 3, 3, 4, 5 ].difference([1, 2, 4]) #=> [ 3, 3, 5 ]
Run Code Online (Sandbox Code Playgroud)

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

Shipment.pluck(:id).difference(Invoice.pluck(:shipment_id))
Run Code Online (Sandbox Code Playgroud)

似乎是解决问题的一种很好的解决方案。我一直是的热心追随者a - b | b - a,尽管有时想起来可能很棘手。

这当然可以解决这个问题。

  • 这很好用。除此之外,值得注意的是,数组的顺序(即您调用“.difference”的数组)在结果中起着重要作用。 (3认同)
  • 我没有得到预期的结果。对我来说它的作用与“a - b”相同。 (2认同)

zhi*_*sme 7

纯红宝石溶液是

(a + b) - (a & b)

([1,2,3,4] + [1,3]) - ([1,2,3,4] & [1,3])
=> [2,4]
Run Code Online (Sandbox Code Playgroud)

哪里a + b会产生两个阵列之间的联合
a & b回报交集
union - intersection会返回差异


6ft*_*Dan 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
Run Code Online (Sandbox Code Playgroud)


6ft*_*Dan 5

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

Shipment.where(["id NOT IN (?)", Invoice.select(:shipment_id)]).select(:id)
Run Code Online (Sandbox Code Playgroud)

它输出SQL

SELECT "shipments"."id" FROM "shipments"  WHERE (id NOT IN (SELECT "invoices"."shipment_id" FROM "invoices"))
Run Code Online (Sandbox Code Playgroud)

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

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).select(:id)
Run Code Online (Sandbox Code Playgroud)

它输出SQL

SELECT "shipments"."id" FROM "shipments"  WHERE ("shipments"."id" NOT IN (SELECT DISTINCT "invoices"."shipment_id" FROM "invoices"))
Run Code Online (Sandbox Code Playgroud)

而不是select(:id)我推荐的ids方法.

Shipment.where.not(id: Invoice.select(:shipment_id).distinct).ids
Run Code Online (Sandbox Code Playgroud)