J'ai une méthode avec le paramètre HashSet. Et je dois faire insensible à la casse Contient en son sein:
public void DoSomething(HashSet<string> set, string item)
{
var x = set.Contains(item);
...
}
Est-il possible de rendre HashSet insensible à la casse (ne pas en créer un nouveau)?
Je recherche une solution avec les meilleures performances.
Modifier
Contient peut être appelé plusieurs fois. Les extensions IEnumerable ne sont donc pas acceptables pour moi en raison de performances inférieures à la méthode native HashSet Contains.
Solution
Depuis, la réponse à ma question est NON, c'est impossible, j'ai créé et utilisé la méthode suivante:
public HashSet<string> EnsureCaseInsensitive(HashSet<string> set)
{
return set.Comparer == StringComparer.OrdinalIgnoreCase
? set
: new HashSet<string>(set, StringComparer.OrdinalIgnoreCase);
}
Le constructeur HashSet<T>
A une surcharge qui vous permet de passer un IEqualityComparer<string>
Personnalisé. Il y en a quelques-uns déjà définis pour vous dans la classe statique StringComparer
, dont certains ignorent la casse. Par exemple:
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
set.Add("john");
Debug.Assert(set.Contains("JohN"));
Vous devrez effectuer cette modification au moment de la construction du HashSet<T>
. Une fois qu'il existe, vous ne pouvez pas modifier le IEqualityComparer<T>
Qu'il utilise.
Juste pour que vous le sachiez, par défaut (si vous ne passez aucun IEqualityComparer<T>
Au constructeur HashSet<T>
), Il utilise à la place EqualityComparer<T>.Default
.
La question semble avoir changé après avoir posté ma réponse. Si vous devez faire un cas insensible rechercher dans un cas existant sensibleHashSet<string>
, Vous devrez faire une recherche linéaire:
set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase));
Il n'y a aucun moyen de contourner cela.
Vous ne pouvez pas par magie faire HashSet (ou dictionnaire) sensible à la casse pour se comporter de manière insensible à la casse.
Vous devez en recréer un à l'intérieur de votre fonction si vous ne pouvez pas compter sur HashSet
entrant pour ne pas respecter la casse.
Code le plus compact - utilisez constructeur à partir de l'ensemble existant:
var insensitive = new HashSet<string>(
set, StringComparer.InvariantCultureIgnoreCase);
Notez que la copie de HashSet
est aussi coûteuse que de parcourir tous les éléments, donc si votre fonction ne fait que la recherche, il serait moins coûteux (O (n)) d'itérer tous les éléments. Si votre fonction est appelée plusieurs fois pour effectuer une recherche insensible à la casse, essayez plutôt de lui passer la bonne HashSet
.
HashSet
est conçu pour trouver rapidement des éléments selon sa fonction de hachage et son comparateur d'égalité. Ce que vous demandez, c'est vraiment de trouver un élément correspondant à "une autre" condition. Imaginez que vous avez un Set<Person>
objets qui n'utilisent que Person.Name
pour comparaison et vous devez trouver un élément avec une valeur donnée de Person.Age
.
Le fait est que vous devez parcourir le contenu de l'ensemble pour trouver les éléments correspondants. Si vous allez le faire souvent, vous pouvez créer un ensemble différent, dans votre cas, en utilisant un comparateur insensible à la casse, mais vous devez vous assurer que cet ensemble d'ombres est synchronisé avec l'original.
Jusqu'à présent, les réponses sont essentiellement des variations de ce qui précède, j'ai pensé ajouter ceci pour clarifier la question fondamentale.
En supposant que vous disposez de cette méthode d'extension:
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
{
return new HashSet<T>(source);
}
Vous pouvez simplement utiliser ceci:
set = set.Select(n => n.ToLowerInvariant()).ToHashSet();
Ou, vous pouvez simplement faire ceci:
set = new HashSet(set, StringComparer.OrdinalIgnoreCase);
//or InvariantCultureIgnoreCase or CurrentCultureIgnoreCase
Le constructeur de HashSet
peut prendre une alternative IEqualityComparer
qui peut remplacer la façon dont l'égalité est déterminée. Voir la liste des constructeurs ici .
La classe StringComparer
contient un tas d'instances statiques de IEqualityComparers
pour les chaînes. En particulier, vous êtes probablement intéressé par StringComparer.OrdinalIgnoreCase
. Ici est la documentation de StringComparer
.
Notez qu'un autre constructeur prend un IEnumerable
, vous pouvez donc construire un nouveau HashSet
à partir de votre ancien, mais avec le IEqualityComparer
.
Donc, tous ensemble, vous voulez convertir votre HashSet
comme suit:
var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);
Si vous souhaitez laisser la version originale, sensible à la casse, vous pouvez simplement l'interroger avec linq avec une insensibilité à la casse:
var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));