Estoy usando Ruby 1.8.6 con Rails 1.2.3 y necesito determinar si dos matrices tienen los mismos elementos, independientemente de si están o no en el mismo orden. Se garantiza que una de las matrices no contiene duplicados (la otra podría, en cuyo caso la respuesta es no).
Mi primer pensamiento fue
require 'set'
a.to_set == b.to_set
pero me preguntaba si había una forma más eficiente o idiomática de hacerlo.
ruby
arrays
comparison
Taymon
fuente
fuente
[1,2]
y[2,1,1]
tienen los mismos elementos?)difference
que ofrece una solución muy rápida y muy legible. Más info aquí.Respuestas:
Esto no requiere conversión para configurar:
fuente
.uniq.sort
entonces? Ademásuniq
es similar ato_set
internamente más adicional.to_a.sort
uniq
s. En realidad terminé creando una de las matrices conRange#to_a
, así que solo tuvesort
la otra.para dos matrices A y B: A y B tienen el mismo contenido si:
(A-B).blank? and (B-A).blank?
o simplemente puede verificar:
((A-B) + (B-A)).blank?
También como lo sugiere @ cort3z, esta solución también funciona para matrices polimórficas, es decir
A = [1 , "string", [1,2,3]] B = [[1,2,3] , "string", 1] (A-B).blank? and (B-A).blank? => true # while A.uniq.sort == B.uniq.sort will throw error `ArgumentError: comparison of Fixnum with String failed`
::::::::::: EDITAR :::::::::::::
Como se sugiere en los comentarios, la solución anterior falla para los duplicados, aunque según la pregunta, eso ni siquiera es obligatorio, ya que el autor de la pregunta no está interesado en los duplicados (está convirtiendo sus matrices para establecer antes de verificar y eso enmascara los duplicados e incluso si miras la respuesta aceptada está usando un operador .uniq antes de verificar y eso también enmascara los duplicados). Pero aún así, si le interesan los duplicados, solo agregar una verificación de conteo solucionará lo mismo (según la pregunta, solo una matriz puede contener duplicados). Entonces la solución final será:
A.size == B.size and ((A-B) + (B-A)).blank?
fuente
A=[1]
yB=[1,1]
, ambos(A-B)
y(B-A)
volverán en blanco. Consulte la documentación de la matriz .a.to_set == b.to_set
y la respuesta aceptada usaa.uniq.sort == b.uniq.sort
y ambos dan exactamente el mismo resultado que((A-B) + (B-A)).blank?
para A = [1] y B = [1,1] de acuerdo? Como solo estaba pidiendo una mejora con respecto a su solución original, mi solución original todavía funciona :). ¿de acuerdo?A = [123, "test", [], some_object, nil]
yB = A#because I am lazy
, luegoA.uniq.sort
arrojará un error (la comparación de la cadena y la matriz fallaron).A = [1, 1, 2]
yB = [1, 2, 2]
Comparaciones de velocidad
require 'benchmark/ips' require 'set' a = [1, 2, 3, 4, 5, 6] b = [1, 2, 3, 4, 5, 6] Benchmark.ips do |x| x.report('sort') { a.sort == b.sort } x.report('sort!') { a.sort! == b.sort! } x.report('to_set') { a.to_set == b.to_set } x.report('minus') { ((a - b) + (b - a)).empty? } end Warming up -------------------------------------- sort 88.338k i/100ms sort! 118.207k i/100ms to_set 19.339k i/100ms minus 67.971k i/100ms Calculating ------------------------------------- sort 1.062M (± 0.9%) i/s - 5.389M in 5.075109s sort! 1.542M (± 1.2%) i/s - 7.802M in 5.061364s to_set 200.302k (± 2.1%) i/s - 1.006M in 5.022793s minus 783.106k (± 1.5%) i/s - 3.942M in 5.035311s
fuente
sort
la velocidadto_set
comenzar a tener un rendimiento superior con matrices lo suficientemente grandes donde O (n logn) comenzaría a importar más que el esfuerzo requerido para convertir una matriz en un conjuntoRuby 2.6+
Ruby se introdujo
difference
en 2.6.Esto da una solución muy rápida y muy legible aquí, como sigue:
a = [1, 2, 3, 4, 5, 6] b = [1, 2, 3, 4, 5, 6] a.difference(b).any? # => false a.difference(b.reverse).any? # => false a = [1, 2, 3, 4, 5, 6] b = [1, 2, 3] a.difference(b).any? # => true
Ejecución de los puntos de referencia:
a = Array.new(1000) { rand(100) } b = Array.new(1000) { rand(100) } Benchmark.ips do |x| x.report('sort') { a.sort == b.sort } x.report('sort!') { a.sort! == b.sort! } x.report('to_set') { a.to_set == b.to_set } x.report('minus') { ((a - b) + (b - a)).empty? } x.report('difference') { a.difference(b).any? } end sort 13.908k (± 2.6%) i/s - 69.513k in 5.001443s sort! 14.656k (± 3.0%) i/s - 73.736k in 5.035744s to_set 5.125k (± 2.9%) i/s - 26.023k in 5.082083s minus 16.398k (± 2.2%) i/s - 83.181k in 5.074938s difference 27.839k (± 5.2%) i/s - 141.048k in 5.080706s
¡Espero que ayude a alguien!
fuente
Cuando los elementos de
a
yb
sonComparable
,Corrección de la respuesta de @ mori basada en el comentario de @ steenslag
fuente
a
yb
se puede ordenar.Si lo espera
[:a, :b] != [:a, :a, :b]
to_set
, no funciona. En su lugar, puede utilizar la frecuencia:class Array def frequency p = Hash.new(0) each{ |v| p[v] += 1 } p end end [:a, :b].frequency == [:a, :a, :b].frequency #=> false [:a, :b].frequency == [:b, :a].frequency #=> true
fuente
a.sort == b.sort
si le importa la frecuencia?["", :b].frequency == [:b, ""].frequency #=> true
a.group_by{|i| i} == b.group_by{|i| i}
Si sabe que las matrices son de la misma longitud y ninguna matriz contiene duplicados, esto también funciona:
Explicación: el
&
operador en este caso devuelve una copia de a1 sin ningún elemento que no se encuentre en a2, que es el mismo que el a1 original si ambos arreglos tienen el mismo contenido sin duplicados.Análisis: Dado que el orden no ha cambiado, supongo que esto se implementa como una iteración doble de manera tan consistente
O(n*n)
, notablemente peor para matrices grandes que lasa1.sort == a2.sort
que deberían funcionar en el peor de los casosO(n*logn)
.fuente
a1 = [1,2,3], a2 = [2, 1, 3]
a1 && a2
devuelve[2,1,3]
para mí que no es igual aa1
a1==a2
? Puede funcionar siarray1
en el lado derecho de la igualdad se reemplaza porarray2
, pero dudo que el orden de los elementos devueltos por&
esté garantizado.&
es un operador de intersección de conjuntos para matrices,&&
es lógico Y, ¡son muy diferentes!combinando
&
ysize
puede ser rápido también.require 'benchmark/ips' require 'set' Benchmark.ips do |x| x.report('sort') { a.sort == b.sort } x.report('sort!') { a.sort! == b.sort! } x.report('to_set') { a.to_set == b.to_set } x.report('minus') { ((a - b) + (b - a)).empty? } x.report('&.size') { a.size == b.size && (a & b).size == a.size } end Calculating ------------------------------------- sort 896.094k (±11.4%) i/s - 4.458M in 5.056163s sort! 1.237M (± 4.5%) i/s - 6.261M in 5.071796s to_set 224.564k (± 6.3%) i/s - 1.132M in 5.064753s minus 2.230M (± 7.0%) i/s - 11.171M in 5.038655s &.size 2.829M (± 5.4%) i/s - 14.125M in 5.010414s
fuente
Un enfoque es iterar sobre la matriz sin duplicados
# assume array a has no duplicates and you want to compare to b !a.map { |n| b.include?(n) }.include?(false)
Esto devuelve una serie de verdades. Si aparece algo falso, el exterior
include?
devolverá verdadero. Por lo tanto, debe invertir todo para determinar si coincide.fuente