【问题标题】:Rails 3. How to get the difference between two arrays?Rails 3. 如何得到两个数组的区别?
【发布时间】:2011-12-26 23:24:48
【问题描述】:

假设我有这个带有货件 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。
  • 货件有一张发票。
  • 因此 invoices 表有一列 shipment_id

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

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

【问题讨论】:

标签: ruby-on-rails ruby arrays


【解决方案1】:
a = [2, 4, 6, 8]
b = [1, 2, 3, 4]

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

【讨论】:

  • 这是最优雅的答案。 a - b 返回 a 中不在 b 中的任何元素。 b - a 返回 b 中不在 a 和 | 中的任何元素从这 2 个结果中返回唯一的元素集。
  • 喜欢这个:) 比较数组差异的好方法
  • 很好的方法,但是,这对重复值不起作用。例如,如果一个包含到两个 4 中,则第二个 4 不会显示
  • 完美解决方案...!
【解决方案2】:

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

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

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

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

注意:记住reject返回一个新数组,使用reject!如果你想改变原来的数组

【讨论】:

【解决方案3】:

使用替代符号

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

【讨论】:

  • 这个:[2, 1] - [1, 2, 3, 2, 6, 7] 返回[]。所以这让我很好奇,无论它们的顺序如何,如何从两个动态数组中获得差异。
  • @Trip 来回答您的问题,您可以这样做...(a-b) + (b-a) 您可以在其中获得两个数组中的唯一值,然后将这些值组合成一个数组。
  • @Ryan 感谢您在此评论线程上保持理智。
【解决方案4】:

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 的忠实追随者,尽管有时很难回忆起来。

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

【讨论】:

  • 我没有得到预期的结果。它对我来说和a - b 一样。
  • 这很好用。除此之外,重要的是要注意数组的顺序(即您调用 .difference 的顺序)在结果中起着重要作用。
【解决方案5】:

纯红宝石溶液是

(a + b) - (a &amp; b)

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

a + b 将在两个数组之间产生联合
a &amp; b返回交集
union - intersection 将返回差异

【讨论】:

    【解决方案6】:

    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
    

    【讨论】:

      【解决方案7】:

      这应该在一个 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"))
      

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

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

      【讨论】:

        【解决方案8】:

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

        在这种情况下,我们可以使用 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"]
        # ]
        

        【讨论】:

          【解决方案9】:
          s.select{|x| !ids.include? x.id}
          

          【讨论】:

          • 这并没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review
          • 这是已接受答案的选择版本。与使用拒绝相反。我只是没有足够的堆栈溢出代表来评论答案本身
          • 我想这已被否决/标记,因为它没有提供任何上下文(例如这里的 ids 是什么?),解释并且与现有答案没有太大区别。如果有用@FezAbbas 的话,有一个指南可以整理一个可靠的答案here :)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多