web-dev-qa-db-fra.com

Vérifiez si deux tableaux ont le même contenu (dans n'importe quel ordre)

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.

77
Taymon

Cela ne nécessite pas de conversion pour définir:

a.sort == b.sort
121
Mori

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?

38
Sahil Dhankhar

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
19
Morozov

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

14
Jared Beck

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
7
Victor Moroz

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).

6
Nat

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!

4
SRack

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.

1
Ron

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
1
Todoroki