2010-10-16 5 views
75

Ho una lista testList che contiene un gruppo di stringhe. Vorrei aggiungere una nuova stringa nello testList solo se non esiste già nell'elenco. Pertanto, ho bisogno di fare una ricerca senza distinzione tra maiuscole e minuscole della lista e renderla efficiente. Non posso usare Contains perché questo non tiene conto dell'involucro. Inoltre, non desidero utilizzare ToUpper/ToLower per motivi di prestazioni. Mi sono imbattuto in questo metodo, che funziona:Ricerca elenco senza distinzione tra maiuscole e minuscole

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

Questo funziona, ma corrisponde anche alle parole parziali. Se la lista contiene "goat", non posso aggiungere "avena" perché afferma che "avena" è già presente nell'elenco. Esiste un modo per cercare in modo efficiente gli elenchi in un modo maiuscole e minuscole, in cui le parole devono corrispondere esattamente? grazie

risposta

105

Invece di String.IndexOf, utilizzare String.Equals per assicurarsi di non avere corrispondenze parziali. Inoltre non utilizzare FindAll poiché passa attraverso ogni elemento, usa FindIndex (si ferma sul primo che colpisce).

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

In alternativa utilizzare alcuni metodi di LINQ (che si ferma anche il primo che colpisce)

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

solo aggiungere, in alcuni test rapidi, sembra che il primo metodo è più veloce di circa il 50%. Forse qualcun altro può confermarlo/negarlo. – Brap

+4

A partire da .NET 2.0, questo è ora fatto facilmente - guarda la risposta di shaxby qui sotto. – Joe

+3

Il metodo Contains Il riferimento a shaxby (che ha un overload che prende un IEqualityComparer) fa parte di LINQ, quindi non è certamente disponibile da .NET 2.0. Solo la classe StringComparer è in circolazione da un po 'di tempo. L'elenco non ha questo metodo, né ArrayList o StringCollection (cose a cui avrebbe potuto facilmente fare riferimento come "elenco"). –

0

si sta controllando se il risultato di IndexOf è maggiore o uguale a 0, il che significa che inizia la partita ovunque nella stringa. Prova a controllare se è uguale -0:

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

Ora "capra" e "d'avena" non corrisponderà, ma "capra" e la volontà "goa". Per evitare ciò, è possibile confrontare le lunghezze delle due stringhe.

Per evitare tutte queste complicazioni, è possibile utilizzare un dizionario anziché un elenco. La loro chiave sarebbe la stringa minuscola e il valore sarebbe la vera stringa. In questo modo, le prestazioni non sono danneggiate perché non è necessario utilizzare ToLower per ciascun confronto, ma è comunque possibile utilizzare Contains.

11

Sulla base di Adam Sills risposta di cui sopra - qui è un bel metodo di estensioni pulita per Contiene ... :)

///---------------------------------------------------------------------- 
/// <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); 
} 
0

ho avuto un problema simile, avevo bisogno l'indice della voce, ma doveva essere caso insensibile, ho guardato tutto il web per un paio di minuti e trovato nulla, così ho scritto un piccolo metodo per farlo fare, ecco quello che ho fatto:

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()); 
} 

Aggiungere questo codice al file stesso, e la chiamata così:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind); 

Spero che questo aiuti, buona fortuna!

+0

perché produrre un secondo elenco? Non è molto efficiente. for (var i = 0; i wesm

+0

Credo che non lo sapremo mai. – Denny

204

Mi rendo conto che questo è un vecchio post, ma solo nel caso in cui nessun altro è alla ricerca, si possibile uso Contains fornendo il case insensitive di confronto di uguaglianza stringa in questo modo:

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

Questo è disponibile dal .net 2.0 secondo msdn.

+17

Sicuramente la migliore risposta qui fuori. :) – Joe

+18

Enumerable . Contiene (cosa stai facendo riferimento) non è stato in giro da. NET 2.0. Nessuna lista . Contiene il sovraccarico che si sta utilizzando. –

+0

@AdamSills right. Non esiste un metodo con tali contenuti nell'elenco . E se si tratta di una collezione pigra, allora può iterarla un paio di volte come fanno gli altri Enumerable . Imho, questo metodo non dovrebbe essere usato per questi casi, in quanto non è così logico per quel caso. –

0

Sulla base di Lance Larsen risposta - Ecco un metodo di estensione con l'string.Compare consigliato invece di string.Equals

Si consiglia vivamente di utilizzare un sovraccarico di String.Compare che prende un parametro StringComparison. Non solo questi sovraccarichi ti permettono di definire il comportamento di confronto esatto che intendi, usarli renderà il tuo codice più leggibile per altri sviluppatori. [Josh Free @ BCL Team Blog]

public static bool Contains(this List<string> source, string toCheck, StringComparison comp) 
{ 
    return 
     source != null && 
     !string.IsNullOrEmpty(toCheck) && 
     source.Any(x => string.Compare(x, toCheck, comp) == 0); 
}