2010-03-23 3 views
18

J'essaie d'implémenter un comparateur personnalisé sur deux listes de chaînes et d'utiliser la méthode .Except() linq pour obtenir celles qui ne sont pas une des listes. La raison pour laquelle je fais un comparateur personnalisé est que je dois faire une comparaison "floue", c'est-à-dire qu'une chaîne sur une liste pourrait être incorporée dans une chaîne de l'autre liste.linq Excepté et personnalisé IEqualityComparer

J'ai fait le comparateur

public class ItemFuzzyMatchComparer : IEqualityComparer<string> 
{ 
    bool IEqualityComparer<string>.Equals(string x, string y) 
    { 
     return (x.Contains(y) || y.Contains(x)); 
    } 

    int IEqualityComparer<string>.GetHashCode(string obj) 
    { 
     if (Object.ReferenceEquals(obj, null)) 
      return 0; 
     return obj.GetHashCode(); 
    } 
} 

suivante Quand je debug, le seul point d'arrêt qui frappe est la méthode GetHashCode(). Les égales() ne sont jamais touchées. Des idées?

+0

Pour moi, ce fut un bon exercice. Dans mon cas, je me suis échappé avec 'public int GetHashCode (chaîne obj) {return obj.ToLower(). GetHashCode();}' Votre question est ancienne mais j'ai rencontré le même problème 4 ans plus tard. –

Répondre

18

Si tous les codes de hachage retournés sont différents, il n'a jamais besoin de comparer pour l'égalité.

Fondamentalement, le problème est que vos concepts de hachage et d'égalité sont très différents. Je ne suis pas tout à fait sûr de la façon dont vous corrigeriez cela, mais tant que vous ne l'aurez pas fait, cela ne fonctionnera certainement pas.

Vous devez vous assurer que si Equals(a, b) renvoie la valeur true, alors GetHashCode(a) == GetHashCode(b). (L'inverse ne doit pas être vrai - les collisions de hachage sont acceptables, bien que, évidemment, vous souhaitiez en avoir le moins possible.)

+0

Je commence à penser qu'il s'agit d'un cas de tentative d'application d'une comparaison personnalisée sur une collection d'objets prédéfinis (c'est-à-dire des chaînes). Si je devais avoir des collections d'objets personnalisés, alors je pourrais probablement le faire fonctionner. Je pense que je vais devoir trouver un meilleur moyen que cela. :(Je laisserai ceci sans réponse pendant un jour pour voir si quelqu'un d'autre a une suggestion – Joe

+0

J'ai fini par le faire en deux étapes J'ai généré une liste d'éléments qui ont partiellement correspondu en utilisant un contenu, puis retourné et fait un sauf utiliser ce sous-ensemble par rapport à la première liste Merci pour votre aide. – Joe

5

Comme Jon l'a fait remarquer, vous devez vous assurer que le code de hachage de deux chaînes égales (selon votre règle de comparaison). C'est malheureusement très difficile. Pour démontrer le problème, Equals(str, "") renvoie true pour toutes les chaînes str, ce qui signifie essentiellement que toutes les chaînes sont égales à une chaîne vide et que, par conséquent, toutes les chaînes doivent avoir le même code de hachage qu'une chaîne vide. Par conséquent, la seule façon de mettre en œuvre correctement IEqualityComparer est de revenir toujours le même hachage code:

public class ItemFuzzyMatchComparer : IEqualityComparer<string> { 
    bool IEqualityComparer<string>.Equals(string x, string y) { 
    return (x.Contains(y) || y.Contains(x)); 
    } 
    int IEqualityComparer<string>.GetHashCode(string obj) { 
    if (Object.ReferenceEquals(obj, null)) return 0; 
    return 1; 
    } 
} 

Ensuite, vous pouvez utiliser la méthode Except et il se comportera correctement. Le seul problème est que vous aurez (probablement) une implémentation plutôt inefficace, donc si vous avez besoin de meilleures performances, vous devrez peut-être implémenter votre propre Except. Cependant, je ne suis pas exactement sûr de l'inefficacité de l'implémentation LINQ et je ne suis pas sûr qu'il soit réellement possible d'avoir une implémentation efficace pour votre règle de comparaison.

1

Peut-être que ce problème pourrait être résolu sans l'implémentation de l'interface IEqualityComparer. Jon et Thomas ont de bons points à propos de l'implémentation de cette interface, et l'égalité ne semble pas définir votre problème. De votre description, je pense que vous pourriez le faire sans utiliser l'extension Except pendant la comparaison. Au lieu de cela, obtenez les matchs en premier, puis faites l'exception. Voir si cela fait le travail pour vous:

List<String> listOne = new List<string>(){"hard", "fun", "code", "rocks"}; 
List<String> listTwo = new List<string>(){"fund", "ode", "ard"}; 

var fuzzyMatchList = from str in listOne 
         from sr2 in listTwo 
         where str.Contains(sr2) || sr2.Contains(str) 
         select str; 
var exceptList = listOne.Except(fuzzyMatchList); 
Questions connexes