ruby on rails - arreglos - Cómo determinar si una matriz contiene todos los elementos de otra matriz
array ruby (6)
Dependiendo de cuán grandes sean sus matrices, podría considerar un algoritmo eficiente 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
La ordenación cuesta O (n log n) y la comprobación de cada par cuesta O (n), por lo que este algoritmo es O (n log n). Los otros algoritmos no pueden ser más rápidos (asintóticamente) utilizando matrices no ordenadas.
Dado:
a1 = [5, 1, 6, 14, 2, 8]
Me gustaría determinar si contiene todos los elementos de:
a2 = [2, 6, 15]
En este caso, el resultado es false
.
¿Hay algún método incorporado de Ruby / Rails para identificar dicha inclusión de matriz?
Una forma de implementar esto es:
a2.index{ |x| !a1.include?(x) }.nil?
¿Hay una forma mejor y más legible?
Esto se puede lograr haciendo
(a2 & a1) == a2
Esto crea la intersección de ambas matrices, devolviendo todos los elementos de a2
que también están en a1
. Si el resultado es el mismo que a2
, puede estar seguro de que tiene todos los elementos incluidos en a1
.
Este enfoque solo funciona si todos los elementos en a2
son diferentes entre sí en primer lugar. Si hay dobles, este enfoque falla. El de Tempos todavía funciona entonces, por lo que recomiendo sinceramente su enfoque (también es probable que sea más rápido).
Puedes aplicar un parche de mono a la clase Array:
class Array
def contains_all?(ary)
ary.uniq.all? { |x| count(x) >= ary.count(x) }
end
end
prueba
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
Por supuesto, el método se puede escribir como un método estándar solo, por ejemplo
def contains_all?(a,b)
b.uniq.all? { |x| a.count(x) >= b.count(x) }
end
y puedes invocarlo como
contains_all?(%w[a b c c], %w[c c c])
De hecho, después del perfilado, la siguiente versión es mucho más rápida y el código es más corto.
def contains_all?(a,b)
b.all? { |x| a.count(x) >= b.count(x) }
end
Si no hay elementos duplicados o si no te importan, puedes usar la clase Set :
a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false
Detrás de las escenas esto usa
all? { |o| set.include?(o) }
Tal vez esto es más fácil de leer:
a2.all? { |e| a1.include?(e) }
También puede usar la intersección de matriz:
(a1 & a2).size == a1.size
Tenga en cuenta que el size
se usa aquí solo para la velocidad, también puede hacerlo (más lento):
(a1 & a2) == a1
Pero creo que el primero es más legible. Estos 3 son rubí simple (no rieles).
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]
a - b
=> [5, 1, 14, 8]
b - a
=> [15]
(b - a).empty?
=> false