我需要判断一个数组是否包含另一个带有重复项的数组的所有元素。
[1,2,3].contains_all? [1,2] #=> true
[1,2,3].contains_all? [1,2,2] #=> false (this is where (a1-a2).empty? fails)
[2,1,2,3].contains_all? [1,2,2] #=> true
因此,第一个数组必须包含第二个数组中每个唯一元素的数量。
This question answers it for those using an array as a set,但我需要控制重复项。
在Ruby 1.9.3p194上
def bench
puts Benchmark.measure {
10000.times do
[1,2,3].contains_all? [1,2]
[1,2,3].contains_all? [1,2,2]
[2,1,2,3].contains_all? [1,2,2]
end
}
end
结果:
Rohit 0.100000 0.000000 0.100000 ( 0.104486)
Chris 0.040000 0.000000 0.040000 ( 0.040178)
Sergio 0.160000 0.020000 0.180000 ( 0.173940)
sawa 0.030000 0.000000 0.030000 ( 0.032393)
@a1 = (1..10000).to_a
@a2 = (1..1000).to_a
@a3 = (1..2000).to_a
def bench
puts Benchmark.measure {
1000.times do
@a1.contains_all? @a2
@a1.contains_all? @a3
@a3.contains_all? @a2
end
}
end
结果:
Rohit 9.750000 0.410000 10.160000 ( 10.158182)
Chris 10.250000 0.180000 10.430000 ( 10.433797)
Sergio 14.570000 0.070000 14.640000 ( 14.637870)
sawa 3.460000 0.020000 3.480000 ( 3.475513)
答案 0 :(得分:6)
class Array
def contains_all? other
other = other.dup
each{|e| if i = other.index(e) then other.delete_at(i) end}
other.empty?
end
end
答案 1 :(得分:2)
这是一个天真而直接的实现(可能不是最有效的实现)。只计算元素并比较两个元素及其出现次数。
class Array
def contains_all? ary
# group the arrays, so that
# [2, 1, 1, 3] becomes {1 => 2, 2 => 1, 3 => 1}
my_groups = group_and_count self
their_groups = group_and_count ary
their_groups.each do |el, cnt|
if !my_groups[el] || my_groups[el] < cnt
return false
end
end
true
end
private
def group_and_count ary
ary.reduce({}) do |memo, el|
memo[el] ||= 0
memo[el] += 1
memo
end
end
end
[1, 2, 3].contains_all? [1, 2] # => true
[1, 2, 3].contains_all? [1, 2, 2] # => false
[2, 1, 2, 3].contains_all? [1, 2, 2] # => true
[1, 2, 3].contains_all? [] # => true
[].contains_all? [1, 2] # => false
答案 2 :(得分:2)
似乎你需要一个多重集。查看this gem,我认为它可以满足您的需求。
您可以使用is并执行类似的操作(如果交集等于第二个multiset,则第一个包含其所有元素):
@ms1 & @ms2 == @ms2
答案 3 :(得分:1)
计算出现次数并比较它们似乎是显而易见的方法。
class Array
def contains_all? arr
h = self.inject(Hash.new(0)) {|h, i| h[i] += 1; h}
arr.each do |i|
return false unless h.has_key?(i)
return false if h[i] == 0
h[i] -= 1
end
true
end
end
答案 4 :(得分:0)
回答我自己的实现,但绝对想看看有人能否提出更有效的方法。 (我不接受我自己的回答)
class Array
def contains_all?(a2)
a2.inject(self.dup) do |copy, el|
if copy.include? el
index = copy.index el
copy.delete_at index
else
return false
end
copy
end
true
end
end
测试:
1.9.3p194 :016 > [1,2,3].contains_all? [1,2] #=> true
=> true
1.9.3p194 :017 > [1,2,3].contains_all? [1,2,2] #=> false (this is where (a1-a2).empty? fails)
=> false
1.9.3p194 :018 > [2,1,2,3].contains_all? [1,2,2] #=> true
=> true
答案 5 :(得分:0)
此解决方案仅迭代两个列表一次,因此以线性时间运行。但是,如果预计列表非常小,则可能会产生过多的开销。
class Array
def contains_all?(other)
return false if other.size > size
elem_counts = other.each_with_object(Hash.new(0)) { |elem,hash| hash[elem] += 1 }
each do |elem|
elem_counts.delete(elem) if (elem_counts[elem] -= 1) <= 0
return true if elem_counts.empty?
end
false
end
end
答案 6 :(得分:0)
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
以下版本的代码更快更短。</ p>
class Array
def contains_all?(ary)
ary.all? { |x| count(x) >= ary.count(x) }
end
end
答案 7 :(得分:-1)
如果找不到方法,可以使用ruby的 include? em>方法构建方法。
官方文件:http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-include-3F
用法:
array = [1, 2, 3, 4]
array.include? 3 #=> true
然后,你可以做一个循环:
def array_includes_all?( array, comparision_array )
contains = true
for i in comparision_array do
unless array.include? i
contains = false
end
end
return contains
end
array_includes_all?( [1,2,3,2], [1,2,2] ) #=> true