Comment déterminer si un tableau contient tous les éléments d'un autre tableau


143

Étant donné:

a1 = [5, 1, 6, 14, 2, 8] 

je voudrais déterminer si elle contient tous les éléments de:

a2 = [2, 6, 15] 

Dans ce cas, le résultat est false . Existe-t-il des méthodes Ruby/Rails intégrées pour identifier une telle inclusion de réseau?

Une façon de mettre en œuvre c'est:

a2.index{ |x| !a1.include?(x) }.nil? 

Y at-il une meilleure, plus facile à lire, façon?

  0

La réponse acceptée (soustraction de tableau) est la solution la plus rapide. Je les ai tous comparés ici: https://gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f 08 mars. 182018-03-08 16:04:45

250
a = [5, 1, 6, 14, 2, 8] 
b = [2, 6, 15] 

a - b 
=> [5, 1, 14, 8] 

b - a 
=> [15] 

(b - a).empty? 
=> false 
+51

C'est le chemin à parcourir. Il pourrait juste être un peu raccourci à '(a2-a1) .empty' 12 sept.. 112011-09-12 12:43:41

  0

Nice et élégant, up vote pour vous =) 12 sept.. 112011-09-12 13:15:44

+5

Cela ne fonctionne que pour les tableaux qui sont des ensembles, et non pour les tableaux avec les doublons 25 nov.. 122012-11-25 17:58:20

  0

@ Chris - Vous pourriez essayez d'utiliser Array # uniq pour cela. Avec l'exemple de Holger Just, ce serait '(a2.uniq - a1.uniq) .empty?' 04 déc.. 122012-12-04 17:07:40

  0

http://stackoverflow.com/questions/13553822/determine-if-one-array-contains-all-elements-of -un autre tableau est ce que je voulais dire. Array # unique va explicitement échouer. 05 déc.. 122012-12-05 14:54:39


60

Peut-être est plus facile à lire:

a2.all? { |e| a1.include?(e) } 

Vous pouvez également utiliser le tableau intersection:

(a1 & a2).size == a1.size 

Notez que size est utilisé ici juste pour la vitesse, vous pouvez également faire (plus lent):

(a1 & a2) == a1 

Mais je suppose que le premier est plus lisible. Ces 3 sont rubis simple (pas de rails).

  0

Je tapais le même. +1 pour une réponse rapide 12 sept.. 112011-09-12 12:37:46

+2

+1 pour la solution la plus lisible sur le dessus. 05 mars. 132013-03-05 16:46:33

  0

J'aime mieux celui-ci. 18 nov.. 162016-11-18 14:31:17

  0

Si vous utilisez la définition OP de a1 et a2, et a1 "contenant tous les éléments de" a2, je pense que cela devrait être _ (a1 & a2) .size == a2.size _ puisque a2 est le plus petit tableau, qui devrait avoir tous les éléments inclus dans le tableau plus grand (pour obtenir 'vrai') - d'où l'intersection des deux tableaux doit être la même longueur que le tableau plus petit si tous les éléments sont présents dans le plus grand. 26 août. 172017-08-26 10:47:19


47

Ceci peut être réalisé en faisant

(a2 & a1) == a2 

Cela crée l'intersection des deux tableaux, tous les éléments de retour a2 qui sont également a1. Si le résultat est le même que a2, vous pouvez être sûr que tous les éléments sont inclus dans .

Cette approche ne fonctionne que si tous les éléments a2 sont différents les uns des autres en premier lieu. S'il y a des doubles, cette approche échoue. Celui de Tempos fonctionne toujours, alors je recommande vivement son approche (aussi c'est probablement plus rapide).

+2

en utilisant la méthode 'longueur' sera beaucoup mieux 12 sept.. 112011-09-12 12:48:42

+2

Cela ne fonctionnera pas si l'ensemble d'intersection a les mêmes éléments dans un ordre différent. Je l'ai trouvé à la dure tout en essayant de répondre à cette question: http://stackoverflow.com/questions/12062970/array-permutations-and-comparisons-does-this-array-contain-any-of-these-arrays/ 12077841 # 12077841 réalisé plus tard beaucoup de gens intelligents l'avaient déjà fait ici! 22 août. 122012-08-22 21:23:36

+1

@CubaLibre Intéressant. Avez-vous des données de test pour reproduire cela? D'après mes tests, il semble que le tableau résultant conserve l'ordre des éléments du premier tableau (d'où ma plus récente modification de ma réponse). Cependant, si ce n'est pas le cas, j'aimerais apprendre. 23 août. 122012-08-23 07:12:05

  0

@HolgerJust J'avais fait l'erreur de faire (a1 & a2) au lieu de (a2 & a1), c'est pourquoi je voyais l'erreur. Vous avez raison et conserver la commande du premier tableau. 23 août. 122012-08-23 13:35:14


9

Si on n'y a pas de doublons ou vous ne se soucient pas eux, alors vous pouvez utiliser la classe Set:

a1 = Set.new [5, 1, 6, 14, 2, 8] 
a2 = Set.new [2, 6, 15] 
a1.subset?(a2) 
=> false 

les coulisses utilise ce

all? { |o| set.include?(o) } 

0

En fonction de la taille de vos tableaux, vous pouvez envisager un algorithme efficace 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 

Le tri coûte O (n log n) et la vérification de chaque paire coûte O (n), donc cet algorithme est O (n log n). Les autres algorithmes ne peuvent pas être plus rapides (asymptotiquement) en utilisant des tableaux non triés.

  0

Vous pouvez le faire en O (n) avec un tri par comptage. 12 sept.. 112011-09-12 17:48:28

  0

Non, vous ne pouvez pas. Le tri par comptage utilise un univers limité et Ruby n'a pas de limitation sur la façon dont les grands nombres peuvent obtenir. 12 sept.. 112011-09-12 18:11:46

  0

Vous pouvez, parce que vous n'avez pas réellement à trier les éléments - vous avez juste besoin d'un élément de mappage de hachage -> compte pour les deux tableaux, puis itérer sur les clés et comparer les comptes. 12 sept.. 112011-09-12 18:13:24

  0

Etes-vous sûr que Array # sort utilise le tri par fusion? 23 avril. 142014-04-23 01:10:01


0

Vous pouvez singe-patcher la classe Array:

class Array 
    def contains_all?(ary) 
     ary.uniq.all? { |x| count(x) >= ary.count(x) } 
    end 
end 

Test

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 

Bien sûr, la méthode peut être écrite comme une méthode standard seule, par exemple

def contains_all?(a,b) 
    b.uniq.all? { |x| a.count(x) >= b.count(x) } 
end 

et vous pouvez l'appeler comme

contains_all?(%w[a b c c], %w[c c c]) 

En effet, après le profilage, la version suivante est beaucoup plus rapide, et le code est plus court.

def contains_all?(a,b) 
    b.all? { |x| a.count(x) >= b.count(x) } 
end