如何确定一个数组是否包含另一个数组的所有元素


178

鉴于:

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

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

a2 = [2, 6, 15]

在这种情况下,结果为false

是否有内置的Ruby / Rails方法来识别这种数组包含?

一种实现方法是:

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

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


可接受的答案(数组减法)是最快的解决方案。我在这里对所有基准进行了基准测试:gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
brainbag

Answers:


308
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

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

b - a
=> [15]

(b - a).empty?
=> false

60
这是要走的路。可能会缩短为(a2-a1).empty?
Holger Just

9
这仅适用于集合数组,不适用于具有重复项的数组
克里斯

3
@Chris-您可以尝试使用Array#uniq。以Holger Just为例,应该是(a2.uniq - a1.uniq).empty?
Nick

stackoverflow.com/questions/135538​​22/…是我的意思。Array#unique将明确地使该操作失败。
克里斯(Chris)

81

也许这更容易阅读:

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是较小的数组,应该具有所有较大数组中包含的元素(以获得“真”)-因此,如果数组中的所有元素都存在于较大数组中,则两个数组的交点应与较小数组的长度相同。
JosephK

57

这可以通过以下方式实现

(a2 & a1) == a2

这将创建两个数组的交集,并返回a2in中的所有元素a1。如果结果与相同a2,则可以确保包含了所有元素a1

仅当其中的所有元素a2互不相同时,此方法才有效。如果有双打,此方法将失败。Tempos的那个仍然可以使用,所以我全力推荐他的方法(可能还更快)。


2
使用该length方法的效果会更好
Pablo Fernandez

3
如果相交集具有相同元素但顺序不同,则此方法将无效。在尝试回答此问题时,我发现了一个困难的方法:stackoverflow.com/questions/12062970/…后来意识到许多聪明的人已经在这里完成了!
CubaLibre 2012年

1
@CubaLibre有趣。您有一些测试数据可以重现吗?在我的测试中,结果数组似乎保留了第一个数组中元素的顺序(因此,我对答案的最新编辑)。但是,如果确实不是这种情况,我想学习。
Holger

@HolgerJust我犯了做(a1&a2)而不是(a2&a1)的错误,这就是为什么我看到此错误。您是正确的并保留第一个数组的顺序。
CubaLibre 2012年

10

如果没有重复的元素或您不在乎它们,则可以使用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) }

1

您可以猴子修补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])

的确,分析后,以下版本速度更快,并且代码更短。

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

0

根据阵列的大小,您可以考虑一种有效的算法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)中进行计数排序。
klochner 2011年

你不能。计数排序使用的是有限的宇宙,而Ruby对可以得到的数字没有限制。
ayckoster 2011年

可以,因为您实际上不必对项目进行排序-您只需要一个哈希映射项目->为两个数组计数,然后遍历键并比较计数即可。
klochner 2011年

您确定Array#sort使用合并排序吗?
Nate Symer 2014年

0

如果任一数组中都有重复的元素,则基于(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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.