【问题标题】:How to determine if one array contains all elements of another array如何确定一个数组是否包含另一个数组的所有元素
【发布时间】:2011-09-12 12:34:12
【问题描述】:

给定:

a1 = [5, 1, 6, 14, 2, 8]

我想确定它是否包含以下所有元素:

a2 = [2, 6, 15]

在这种情况下,结果是false

是否有任何内置的 Ruby/Rails 方法来识别此类数组包含?

实现这一点的一种方法是:

a2.index{ |x| !a1.include?(x) }.nil?

有没有更好、更易读的方法?

【问题讨论】:

标签: ruby-on-rails arrays ruby


【解决方案1】:
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

a - b
# => [5, 1, 14, 8]

b - a
# => [15]

(b - a).empty?
# => false

【讨论】:

  • 这是要走的路。它可能只是有点缩短为(a2-a1).empty?
  • 这仅适用于集合数组,不适用于具有重复项的数组
  • @Chris - 你可以尝试使用 Array#uniq。以 Holger Just 为例,它将是 (a2.uniq - a1.uniq).empty?
  • stackoverflow.com/questions/13553822/… 是我的意思。 Array#unique 将显式失败。
  • cmets 中的好答案和建议,真的很有帮助!
【解决方案2】:

也许这更容易阅读:

a2.all? { |e| a1.include?(e) }

你也可以使用数组交集:

(a1 & a2).size == a1.size

注意size在这里只是为了速度,你也可以这样做(慢):

(a1 & a2) == a1

但我想第一个更具可读性。这 3 个是纯红宝石(不是铁轨)。

【讨论】:

  • 如果使用 OP 对 a1 和 a2 的定义,以及 a1“包含”a2 的所有元素,我认为这应该是 _ (a1 & a2).size == a2.size _ 因为 a2 是较小的数组,它应该将所有元素都包含在较大的数组中(以获得“真”) - 因此,如果其中的所有元素都存在于较大的数组中,则两个数组的交集的长度应该与较小的数组相同.
【解决方案3】:

这可以通过做来实现

(a2 & a1) == a2

这将创建两个数组的交集,返回来自a2 的所有元素,这些元素也在a1 中。如果结果与a2 相同,则可以确定a1 中包含所有元素。

这种方法只有在a2 中的所有元素首先彼此不同时才有效。如果有双打,这种方法就失败了。来自 Tempos 的那个仍然有效,所以我全心全意地推荐他的方法(也可能更快)。

【讨论】:

  • 使用length 方法会执行得更好
  • 如果交集的相同元素的顺序不同,这将不起作用。我在尝试回答这个问题时发现了这一点:stackoverflow.com/questions/12062970/… 后来意识到很多聪明人已经在这里做到了!
  • @CubaLibre 很有趣。你有一些测试数据来重现这个吗?在我的测试中,结果数组似乎保留了第一个数组中元素的顺序(因此我最近对我的答案进行了编辑)。但是,如果确实不是这样,我想学习。
  • @HolgerJust 我犯了做 (a1 & a2) 而不是 (a2 & a1) 的错误,这就是我看到错误的原因。您是正确的并保留了第一个数组的顺序。
  • 从我有限的测试来看,顺序似乎不再重要。
【解决方案4】:

如果没有重复元素或者你不关心它们,那么你可以使用Set类:

a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false

这个使用的幕后

all? { |o| set.include?(o) }

【讨论】:

  • 我喜欢。在一行中,它将是 a1.to_set.subset?(a2.to_set)
【解决方案5】:

您可以对 Array 类进行猴子补丁:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

测试

irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true

当然,该方法可以写成一个单独的标准方法,例如

def contains_all?(a,b)
    b.uniq.all? { |x| a.count(x) >= b.count(x) }
end

你可以像这样调用它

contains_all?(%w[a b c c], %w[c c c])

确实,经过profiling之后,下面的版本快多了,代码也更短了。

def contains_all?(a,b)
    b.all? { |x| a.count(x) >= b.count(x) }
end

【讨论】:

    【解决方案6】:

    根据你的数组有多大,你可能会考虑一个有效的算法 O(n log n)

    def equal_a(a1, a2)
      a1sorted = a1.sort
      a2sorted = a2.sort
      return false if a1.length != a2.length
      0.upto(a1.length - 1) do 
        |i| return false if a1sorted[i] != a2sorted[i]
      end
    end
    

    排序成本 O(n log n) 并且检查每一对成本 O(n) 因此这个算法是 O(n log n)。使用未排序的数组,其他算法不能更快(渐近)。

    【讨论】:

    • 你可以用计数排序在 O(n) 中完成。
    • 不,你不能。计数排序使用有限的宇宙,而 Ruby 对可以得到多大的数字没有限制。
    • 你可以,因为你实际上不必对项目进行排序——你只需要一个哈希映射项目——>对两个数组进行计数,然后遍历键并比较计数。
    • 你确定 Array#sort 使用归并排序吗?
    【解决方案7】:

    如果任一数组中存在重复元素,大多数基于 (a1 - a2) 或 (a1 & a2) 的答案都将不起作用。我来到这里是为了寻找一种方法来查看一个单词的所有字母(拆分为一个数组)是否是一组字母的一部分(例如拼字游戏)。这些答案都没有奏效,但这个答案:

    def contains_all?(a1, a2)
      try = a1.chars.all? do |letter|
        a1.count(letter) <= a2.count(letter)
      end
      return try
    end
    

    【讨论】:

      【解决方案8】:

      当我试图查找一个数组 ["a", "b", "c"] 是否包含另一个数组 ["a", "b"] 时,我被定向到这篇文章,在我的情况下,相同的顺序是该问题的附加要求。

      这是我的解决方案(我相信这是 O(n) 复杂度),任何有额外要求的人:

      def array_includes_array(array_to_inspect, array_to_search_for)
        inspectLength = array_to_inspect.length
        searchLength = array_to_search_for.length
      
        if searchLength == 0 then
          return true
        end
      
        if searchLength > inspectLength then
          return false
        end
      
        buffer = []
      
        for i in 0..inspectLength
          buffer.push(array_to_inspect[i])
      
          bufferLastIndex = buffer.length - 1
          if(buffer[bufferLastIndex] != array_to_search_for[bufferLastIndex]) then
            buffer.clear
            next
          end
      
          if(buffer.length == searchLength) then
            return true
          end
        end
      
        return false
      end
      

      这会产生测试结果:

      puts "1: #{array_includes_array(["a", "b", "c"], ["b", "c"])}" # true
      puts "2: #{array_includes_array(["a", "b", "c"], ["a", "b"])}" # true
      puts "3: #{array_includes_array(["a", "b", "c"], ["b", "b"])}" # false
      puts "4: #{array_includes_array(["a", "b", "c"], ["c", "b", "a"])}" # false
      puts "5: #{array_includes_array(["a", "b", "c"], [])}" # true
      puts "6: #{array_includes_array([], ["a"])}" # false
      puts "7: #{array_includes_array([], [])}" # true
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-01
        • 2021-06-04
        • 1970-01-01
        • 2021-12-30
        • 2019-05-05
        相关资源
        最近更新 更多