2011-03-02 2 views
14

Supposons que j'ai deux collections comme suit:Vérifiez si une collection de valeurs contient un autre

Collection1: "A1" "A1" "M1" "M2"

Collection2: « M2 " "M3" "M1" "A1" "A1" "A2"

toutes les valeurs sont des valeurs de chaîne. Je veux savoir si tous les éléments de Collection1 sont contenus dans Collection2, mais je n'ai aucune garantie sur la commande et un ensemble peut avoir plusieurs entrées avec la même valeur. Dans ce cas, Collection2 contient Collection1 parce que Collection2 a deux A1, M1 et M2. Theres la façon évidente: triant les deux collections et les valeurs de saut à mesure que je trouve des correspondances, mais je me demandais s'il existe une manière plus rapide et plus efficace de le faire. Encore une fois avec les collections initiales je ne garantie sur l'ordre ou combien de fois une valeur donnée apparaîtrai

EDIT: Modification ensemble à la collecte juste pour éclaircir que ceux-ci ne sont pas ensembles, car ils peuvent contenir des valeurs en double

+0

conjecture totale hors du bleu, est-ce devoir (ou peut-être une question d'entrevue)? – Mehrdad

+0

Nah, j'écris une logique pour un jeu, et je veux ajouter une fonctionnalité où un tas d'actions/attaques peut être empilé ensemble puis réduit en un différent – Megatron

+0

@ user127817: Haha d'accord, désolé! Nous posons souvent cette question (pour éviter de donner une réponse directe à un problème de devoirs), et j'imagine que cela devient assez ennuyeux pour les utilisateurs qui ne posent pas de questions sur leurs devoirs. Question interessante! :) – Mehrdad

Répondre

16

Oui, il y a un moyen plus rapide, à condition que vous n'ayez pas d'espace limité. (Voir space/time tradeoff.)

L'algorithme:

Il suffit d'insérer tous les éléments Set2 dans un Hashtable (en C# 3.5, qui est un HashSet<string>), puis passer par tous les éléments de Set1 et vérifier si elles re dans la hashtable. Cette méthode est plus rapide (time (m + n) complexité temporelle), mais utilise l'espace O (n).

Sinon, dites:

bool isSuperset = new HashSet<string>(set2).IsSupersetOf(set1); 

Edit 1:

Pour les personnes concernées par la possibilité de doublons (et donc le terme impropre "ensemble"), l'idée peut facilement être étendu:

Juste faire un nouveau Dictionary<string, int> représentant le nombre de chaque mot dans la super-liste (ajouter un au compte chaque fois que vous voyez une instance d'un mot existant, et ajoutez le mot avec un nombre de 1 si ce n'est pas dans le dictionnaire), puis parcourez la sous-liste et décrémentez le compte à chaque fois. Si chaque mot existe dans le dictionnaire et le compte n'est jamais nul lorsque vous essayez de le décrémenter, alors le sous-ensemble est en fait une sous-liste; Sinon, vous avez trop d'instances d'un mot (ou il n'existe pas du tout), donc ce n'est pas une vraie sous-liste.


Edit 2:

Si les chaînes sont très grandes et vous êtes préoccupés par l'efficacité de l'espace, et un algorithme qui fonctionne avec (très) forte probabilité fonctionne pour vous, essayez de stocker une hacher de chaque chaîne à la place. Il n'est techniquement pas garanti de fonctionner, mais la probabilité qu'il ne fonctionne pas est très faible.

+0

Il suffit d'utiliser ['IsSubsetOf'] (http://msdn.microsoft.com/en-us/library/bb358446.aspx) :) – porges

+0

@Porges: Edit: Je pensais que vous vouliez dire que' IsSubsetOf' est un LINQ méthode, mais ce n'était pas - est-ce vraiment la méthode que vous vouliez dire, ou vouliez-vous dire «IsSupersetOf»? (Je pense qu'utiliser 'IsSubsetOf' sur le sous-ensemble est plus lent que d'utiliser' IsSupersetOf' sur le surensemble.) – Mehrdad

+0

Si vous avez des doublons, l'utilisation de jeux et la théorie des ensembles ne sont pas la solution. "Un ensemble est une collection qui ne contient aucun élément en double" et la logique fait cette hypothèse. Si vous supprimez le second A1 de Set2, les deux A1 de Set1 seront toujours considérés comme "in" Set2. –

3

Découvrez linq. ..

string[] set1 = {"A1", "A1", "M1", "M2" }; 
string[] set2 = { "M2", "M3", "M1", "A1", "A1", "A2" }; 

var matching = set1.Intersect(set2); 

foreach (string x in matching) 
{ 
    Console.WriteLine(x); 
} 
+0

+1 le meilleur choix. –

+0

J'ai trouvé que LINQ était lent en pratique, même si la complexité temporelle théorique reste optimale. : \ (Les itérateurs sont un gros goulot d'étranglement parfois.) – Mehrdad

+0

Cela ne résout pas le problème de l'OP - le problème est que "collection2 contient tous les éléments de collection1, en tenant compte des doublons." Intersect() retourne juste UNE de chaque chaîne dans set1 Ceci est dans set2, ie {"A1", "M1", "M2"} – saus

5

Le problème que je vois avec le HashSet, Intersection, et d'autres réponses Set théorie est que vous ne contiennent des doublons, et « Un ensemble est une collection qui ne contient aucun élément en double ». Voici un moyen de gérer les cas en double.

var list1 = new List<string> { "A1", "A1", "M1", "M2" }; 
var list2 = new List<string> { "M2", "M3", "M1", "A1", "A1", "A2" }; 

// Remove returns true if it was able to remove it, and it won't be there to be matched again if there's a duplicate in list1 
bool areAllPresent = list1.All(i => list2.Remove(i)); 

EDIT: Je renommé Set1 et Set2 à list1 et list2 pour apaiser Mehrdad.

EDIT 2: Le commentaire l'implique, mais je voulais indiquer explicitement que cela modifie la liste2. Faites-le de cette façon seulement si vous l'utilisez comme comparaison ou contrôle mais que vous n'avez pas besoin du contenu par la suite.

+0

@druttka: +1 pour les appeler 'Set1' et' Set2', même Bien que vous ayez argumenté contre cela ... c'est amusant.: P Et c'est follement lent. – Mehrdad

+0

@Mehrdad J'ai utilisé les noms de son exemple. "Insanely" semble être un terme relatif, et au moins cela fonctionne contrairement aux solutions de théorie des ensembles postées ailleurs. –

+0

@druttka: Ce n'est pas relatif, puisque c'est O (m * n) alors que l'autre solution est O (m + n). Que ce soit un abus de langage ou un autre problème est une question différente, mais cette solution est assez lente à mon humble avis. :( – Mehrdad

0

similaires un

string[] set1 = new string[] { "a1","a2","a3","a4","a5","aa","ab" }; 
string[] set2 = new string[] {"m1","m2","a4","a6","a1" }; 

var a = set1.Select(set => set2.Contains(set)); 
+0

puisque ce n'est pas évident quelles sont les valeurs de retour, vous devriez être explicite avec votre saisie. Qu'est-ce qu'un 'a'? – jeromeyers

+0

Il renvoie la liste (ou Collection ou tout ce que vous voulez l'appeler) de tous les éléments de set1 qui se trouvent dans set2. Par conséquent, il ne vérifie pas correctement si set2 contient TOUS les éléments de set1, car tant que set2 contient 1 élément de set1, 'Any()' sera toujours vrai. –

29

La façon la plus concise que je connaisse:

//determine if Set2 contains all of the elements in Set1 
bool containsAll = Set1.All(s => Set2.Contains(s)); 
+0

Clairement la meilleure réponse. Je ne sais pas comment il mesure sur perf. mais dans ma situation, c'est parfait. – jeromeyers

+0

Si vous voulez déterminer si Set1 et Set2 contiennent les mêmes éléments indépendamment de l'ordre que vous pouvez faire: if (Set1.All (s => Set2.Contains (s)) && Set2.All (s => Set1.Contains (s))) {...} – jeromeyers

+0

Excellente solution! Dans le cas où vous avez besoin de savoir quels sont les objets communs entre les collections, vous pouvez utiliser: a.Intersect (b) où a et b sont la collection. –

Questions connexes