174 votes

Recherche de liste sensible à la casse

J'ai une liste testList qui contient un tas de chaînes de caractères. Je voudrais ajouter une nouvelle chaîne dans le fichier testList seulement s'il n'existe pas déjà dans la liste. Par conséquent, je dois effectuer une recherche insensible à la casse dans la liste et la rendre efficace. Je ne peux pas utiliser Contains parce que ça ne tient pas compte de l'enveloppe. Je ne veux pas non plus utiliser ToUpper/ToLower pour des raisons de performance. Je suis tombé sur cette méthode, qui fonctionne :

    if(testList.FindAll(x => x.IndexOf(keyword, 
                       StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
       Console.WriteLine("Found in list");

Cela fonctionne, mais cela correspond aussi à des mots partiels. Si la liste contient "chèvre", je ne peux pas ajouter "avoine" car il prétend que "avoine" est déjà dans la liste. Existe-t-il un moyen d'effectuer une recherche efficace dans les listes sans tenir compte de la casse, les mots devant correspondre exactement ? Merci.

438voto

shaxby Points 357

Je sais que c'est un vieux message, mais au cas où quelqu'un d'autre chercherait, vous peut utiliser Contains en fournissant le comparateur d'égalité de chaînes de caractères insensible à la casse, comme suit :

using System.Linq;

// ...

if (testList.Contains(keyword, StringComparer.OrdinalIgnoreCase))
{
    Console.WriteLine("Keyword Exists");
}

Cette fonction est disponible depuis la version 2.0 de .net, selon le site Web de la Commission européenne. msdn .

23 votes

C'est certainement la meilleure réponse ici. :)

23 votes

Enumerable<T>.Contains (ce à quoi vous faites référence) n'existe plus depuis .NET 2.0. Il n'y a pas de List<T>.Contains qui possède la surcharge que vous utilisez.

0 votes

@AdamSills droit. Il n'y a pas de méthode contains dans List<T>. Et si c'est une collection paresseuse, alors elle peut l'itérer plusieurs fois comme le font les autres méthodes Enumerable<T>. Je pense que cette méthode ne devrait pas être utilisée dans de tels cas, car elle n'est pas très logique dans ce cas.

205voto

Adam Sills Points 8749

Au lieu de String.IndexOf, utilisez String.Equals pour s'assurer que vous n'avez pas de correspondance partielle. N'utilisez pas non plus FindAll, qui passe en revue tous les éléments. FindIndex (il s'arrête sur le premier qu'il touche).

if(testList.FindIndex(x => x.Equals(keyword,  
    StringComparison.OrdinalIgnoreCase) ) != -1) 
    Console.WriteLine("Found in list"); 

Vous pouvez aussi utiliser des méthodes LINQ (qui s'arrêtent également à la première qu'elles rencontrent).

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");

0 votes

Juste pour ajouter, dans quelques tests rapides, il semble que la première méthode soit environ 50% plus rapide. Peut-être que quelqu'un d'autre pourra confirmer ou infirmer cela.

11 votes

Depuis la version 2.0 de .NET, cela est maintenant facile à faire - regardez la réponse de shaxby ci-dessous.

3 votes

La méthode Contains à laquelle shaxby fait référence (qui a une surcharge qui prend un IEqualityComparer) fait partie de LINQ, donc elle n'est certainement pas disponible depuis .NET 2.0. La classe StringComparer existe depuis un certain temps. List<T> n'a pas cette méthode, pas plus que ArrayList ou StringCollection (des choses qu'il aurait pu facilement référencer comme sa 'liste').

17voto

Lance Larsen Points 231

Sur la base de la réponse d'Adam Sills ci-dessus - voici une méthode d'extension propre pour les conteneurs... :)

///----------------------------------------------------------------------
/// <summary>
/// Determines whether the specified list contains the matching string value
/// </summary>
/// <param name="list">The list.</param>
/// <param name="value">The value to match.</param>
/// <param name="ignoreCase">if set to <c>true</c> the case is ignored.</param>
/// <returns>
///   <c>true</c> if the specified list contais the matching string; otherwise, <c>false</c>.
/// </returns>
///----------------------------------------------------------------------
public static bool Contains(this List<string> list, string value, bool ignoreCase = false)
{
    return ignoreCase ?
        list.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase)) :
        list.Contains(value);
}

0voto

Ilya Kogan Points 7357

Vous vérifiez si le résultat d'IndexOf est supérieur ou égal à 0, c'est-à-dire si la correspondance commence. partout dans la chaîne. Essayez de vérifier si c'est égal à 0 :

if (testList.FindAll(x => x.IndexOf(keyword, 
                   StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
   Console.WriteLine("Found in list");

Maintenant, "chèvre" et "avoine" ne correspondront pas, mais "chèvre" et "goa" le feront. Pour éviter cela, vous pouvez comparer la longueur des deux chaînes de caractères.

Pour éviter toute cette complication, vous pouvez utiliser un dictionnaire au lieu d'une liste. La clé serait la chaîne en minuscules, et la valeur serait la vraie chaîne. De cette manière, les performances ne sont pas affectées car vous n'avez pas à utiliser ToLower pour chaque comparaison, mais vous pouvez toujours utiliser Contains .

-2voto

J'ai eu un problème similaire, j'avais besoin de l'index de l'élément mais il devait être insensible à la casse, j'ai cherché sur le web pendant quelques minutes et je n'ai rien trouvé, donc j'ai juste écrit une petite méthode pour y arriver, voici ce que j'ai fait :

private static int getCaseInvariantIndex(List<string> ItemsList, string searchItem)
{
    List<string> lowercaselist = new List<string>();

    foreach (string item in ItemsList)
    {
        lowercaselist.Add(item.ToLower());
    }

    return lowercaselist.IndexOf(searchItem.ToLower());
}

Ajoutez ce code au même fichier, et appelez-le comme ceci :

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

J'espère que cela vous aidera, bonne chance !

1 votes

Pourquoi produire une deuxième liste ? Ce n'est pas très efficace. for(var i = 0 ; i < itemsList.Count ; i++) { if (item.ToLower() == searchItem.ToLower()) { return i } }

0 votes

Je suppose que nous ne le saurons jamais.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X