J'utilise Ruby 1.8.6 avec Rails 1.2.3, et j'ai besoin de déterminer si deux tableaux ont les mêmes éléments, qu'ils soient ou non êtes dans le même ordre: il est garanti que l’un des tableaux ne contient pas de doublons (l’autre pourrait, dans ce cas, la réponse est non).
Ma première pensée a été
require 'set'
a.to_set == b.to_set
mais je me demandais s'il y avait une manière plus efficace ou idiomatique de le faire.
Cela ne nécessite pas de conversion pour définir:
a.sort == b.sort
pour deux tableaux A et B: A et B ont le même contenu si: (A-B).blank? and (B-A).blank?
ou vous pouvez simplement vérifier: ((A-B) + (B-A)).blank?
Comme le suggère également @ cort3z, cette solution fonctionne également pour les tableaux polymorphes, c.-à-d.
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`
::::::::::: MODIFIER :::::::::::::
Comme suggéré dans les commentaires, la solution ci-dessus échoue pour les doublons.Bien que, conformément à la question, le demandeur n'est même pas intéressé par les doublons (il convertit ses tableaux en ensembles avant de vérifier et masque les doublons et même si vous regardez la réponse acceptée, il utilise un opérateur .uniq avant de vérifier et masque également les doublons.). Néanmoins, si les doublons vous intéressent, il vous suffira d’ajouter une vérification du nombre (un seul tableau peut contenir des doublons). La solution finale sera donc: A.size == B.size and ((A-B) + (B-A)).blank?
Les comparaisons de vitesse
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
Lorsque les éléments de a
et b
sont Comparable
,
a.sort == b.sort
Correction de la réponse de @ mori à la suite du commentaire de @ steenslag
Si vous vous attendez à [:a, :b] != [:a, :a, :b]
to_set
ne fonctionne pas. Vous pouvez utiliser la fréquence à la place:
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
Si vous savez que les tableaux ont la même longueur et qu'aucun tableau ne contient de doublons, cela fonctionne aussi:
( array1 & array2 ) == array1
Explication: l'opérateur &
, Dans ce cas, renvoie une copie de a1 sans les éléments non trouvés dans a2, ce qui est identique à l'original a1 si les deux tableaux ont le même contenu sans les doublons.
Analyse: Etant donné que l'ordre est inchangé, j'imagine que ceci est implémenté comme une double itération si cohérente O(n*n)
, notamment pire pour les tableaux de grande taille que a1.sort == a2.sort
devrait fonctionner avec le cas le plus défavorable O(n*logn)
.
Ruby 2.6 +
Ruby a introduit difference
dans 2.6.
Cela donne une solution très rapide et très lisible, comme suit:
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
Lancer les repères:
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
J'espère que ça aide quelqu'un!
Une approche consiste à parcourir le tableau sans dupliquer
# assume array a has no duplicates and you want to compare to b
!a.map { |n| b.include?(n) }.include?(false)
Cela retourne un tableau d’indices. Si un faux apparaît, alors le include?
retournera vrai. Ainsi, vous devez inverser le tout pour déterminer s'il s'agit d'une correspondance.
combinant &
et size
peuvent être rapides aussi.
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