Ответ 1
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,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
Итак, первый массив должен содержать столько или равно числа каждого уникального элемента во втором массиве.
Этот вопрос отвечает на вопрос для тех, кто использует массив как набор, но мне нужно контролировать дубликаты.
В 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)
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
Здесь наивная и простая реализация (не самая эффективная, вероятно). Просто подсчитайте элементы и сравните оба элемента и их количество встречаемости.
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
Кажется, вам нужен мультимножество. Проверьте этот камень, я думаю, что он делает то, что вам нужно.
Вы можете использовать is и делать что-то вроде (если пересечение равно второму мультимножеству, то первое включает в себя все его элементы):
@ms1 & @ms2 == @ms2
Подсчет количества вхождений и их сравнение кажется очевидным.
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
Отвечая на мою собственную реализацию, но определенно хочу узнать, может ли кто-нибудь придумать более эффективный способ. (Я не буду принимать свой собственный ответ)
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
Это решение будет выполнять только итерацию через оба списка один раз и, следовательно, работать в линейном времени. Однако может быть слишком много накладных расходов, если ожидается, что списки будут очень маленькими.
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
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
Следующая версия быстрее и короче в коде.
class Array
def contains_all?(ary)
ary.all? { |x| count(x) >= ary.count(x) }
end
end
Если вы не можете найти метод, вы можете создать его с помощью ruby include? Метод.
Официальная документация: 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