2010-10-16 20 views
75

Tengo una lista testList que contiene un montón de cadenas. Me gustaría agregar una nueva cadena en el testList solo si aún no existe en la lista. Por lo tanto, necesito hacer una búsqueda insensible a mayúsculas y minúsculas de la lista y hacerla eficiente. No puedo usar Contains porque eso no tiene en cuenta la carcasa. Tampoco quiero usar ToUpper/ToLower por razones de rendimiento. Me encontré con este método, que funciona:Búsqueda de lista insensible a mayúsculas/minúsculas

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

Esto funciona, pero también coincide con palabras parciales. Si la lista contiene "cabra", no puedo agregar "avena" porque dice que "avena" ya está en la lista. ¿Hay alguna manera de buscar de manera eficiente las listas de una manera que no distinga entre mayúsculas y minúsculas, donde las palabras tienen que coincidir exactamente? gracias

Respuesta

105

En lugar de String.IndexOf, use String.Equals para asegurarse de que no tiene coincidencias parciales. Además, no use FindAll ya que eso pasa por cada elemento, use FindIndex (se detiene en el primero que golpea).

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

Alternativamente usar algunos métodos de LINQ (que también se detiene en el primero que golpea)

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

Sólo para añadir, en algunas pruebas rápidas, parece que el primer método es de alrededor de 50% más rápido. Tal vez alguien más puede confirmar/negar eso. – Brap

+4

A partir de .NET 2.0, esto ahora es fácil de hacer: consulte la respuesta de shaxby a continuación. – Joe

+3

El método Contiene la referencia de shaxby (que tiene una sobrecarga que toma un IEqualityComparer) es parte de LINQ, por lo que ciertamente no ha estado disponible desde .NET 2.0. Solo la clase StringComparer ha existido por un tiempo. List no tiene ese método, ni tampoco ArrayList o StringCollection (cosas que podría haber estado haciendo referencia fácilmente como su 'lista'). –

0

Estás mirando si el resultado de IndexOf es mayor o igual a 0, es decir, si los arranques de los partidos en cualquier parte en la cadena. Trate de ver si es igual a 0:

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

Ahora "cabra" y "avena" no va a coincidir, pero "cabra" y "Goa". Para evitar esto, puede comparar las longitudes de las dos cadenas.

Para evitar toda esta complicación, puede utilizar un diccionario en lugar de una lista. La clave sería la cadena en minúscula, y el valor sería la cadena real. De esta forma, el rendimiento no se ve afectado porque no tiene que usar ToLower para cada comparación, pero igual puede usar Contains.

11

Sobre la base de Adam Sills respuesta anterior - aquí es un buen método para extensiones limpia 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

tuve un problema similar, que necesitaba el índice del elemento, pero que tenía que ser el caso insensibles, miré alrededor de la web durante unos minutos y no encontré nada, por lo que acabo de escribir un pequeño método para lograr que se haga, esto es lo que hice:

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

añadir este código en el mismo archivo, y la llamada es así:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind); 

Espero que esto ayude, buena suerte!

+0

¿por qué producir una segunda lista? Eso no es muy eficiente. for (var i = 0; i wesm

+0

Supongo que nunca lo sabremos. – Denny

204

me di cuenta que es una entrada antigua, pero en caso de cualquier otra persona está mirando, que puede uso Contains proporcionando el caso insensible comparador de igualdad cadena de este modo:

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

Esto ha estado disponible desde .net 2.0 de acuerdo con msdn.

+17

Definitivamente la mejor respuesta aquí. :) – Joe

+18

Enumerable .Contains (a lo que te refieres) no ha existido desde .NET 2.0. No hay una lista . Contiene la sobrecarga que estás usando. –

+0

@AdamSills correcto. No existe dicho método en la lista . Y si se trata de una colección floja, puede iterarla un par de veces como lo hacen otros métodos de Enumerable . Imho, este método no debería usarse para tales casos, ya que no es tan lógico para ese caso. –

0

Sobre la base de Lance Larsen respuesta - aquí es un método de extensión con el string.Compare recomendada en lugar de string.Equals

Es muy recomendable que utilice una sobrecarga de String.Compare que toma un parámetro StringComparison. Estas sobrecargas no solo le permiten definir el comportamiento de comparación exacto que pretendía, su uso también hará que su código sea más legible para otros desarrolladores. [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); 
} 
Cuestiones relacionadas