Поиск списка без учета регистра - PullRequest
115 голосов
/ 16 октября 2010

У меня есть список testList, который содержит кучу строк. Я хотел бы добавить новую строку в testList, только если она не существует в списке. Поэтому мне нужно выполнить поиск в списке без учета регистра и сделать его эффективным. Я не могу использовать Contains, потому что это не учитывает корпус. Я также не хочу использовать ToUpper/ToLower по соображениям производительности. Я сталкивался с этим методом, который работает:

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

Это работает, но также соответствует частичным словам. Если список содержит «козел», я не могу добавить «овес», потому что он утверждает, что «овес» уже есть в списке. Есть ли способ эффективно искать списки без учета регистра, где слова должны точно совпадать? спасибо

Ответы [ 7 ]

298 голосов
/ 15 марта 2013

Я понимаю, что это старая запись, но на всякий случай, если кто-то еще ищет, вы можете использовать Contains, предоставив компаратор равенства строк без учета регистра, например:

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

Это доступно с .net 2.0 согласно msdn .

148 голосов
/ 16 октября 2010

Вместо String.IndexOf используйте String.Equals , чтобы убедиться, что у вас нет частичных совпадений.Также не используйте FindAll, поскольку он проходит через каждый элемент, используйте FindIndex (он останавливается на первом попадании).

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

Альтернативно используйте некоторые методы LINQ (которые также останавливаютсяпо первому попаданию)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");
17 голосов
/ 21 апреля 2012

Основано на ответе Адама Силлса выше - вот хороший метод чистых расширений для Contains ...:)

///----------------------------------------------------------------------
/// <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);
}
2 голосов
/ 26 февраля 2019

Вы можете использовать StringComparer:

    var list = new List<string>();
    list.Add("cat");
    list.Add("dog");
    list.Add("moth");

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    {
        Console.WriteLine("found");
    }
1 голос
/ 25 апреля 2016

На основе ответа Ланса Ларсена - вот метод расширения с рекомендуемой строкой. Сравните вместо строки. Эквиваленты

Настоятельно рекомендуется использовать перегрузку String.Compare, которая принимает параметр StringComparison. Эти перегрузки не только позволяют вам определить точное поведение при сравнении, но и сделать ваш код более читабельным для других разработчиков. [ Джош Фри @ 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);
}
0 голосов
/ 28 января 2013

У меня была похожая проблема, мне нужен был индекс элемента, но он должен был быть без учета регистра, я просматривал сеть в течение нескольких минут и ничего не нашел, поэтому я просто написал небольшой метод, чтобы сделать это, здесьвот что я сделал:

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

Добавьте этот код в тот же файл и назовите его так:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

Надеюсь, это поможет, удачи!

0 голосов
/ 16 октября 2010

Вы проверяете, является ли результат IndexOf большим или равным 0, что означает, начинается ли совпадение в любом месте в строке. Попробуйте проверить, равно ли оно равно 0:

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

Теперь "коза" и "овес" не будут совпадать, но "коза" и "коза" будут совпадать. Чтобы избежать этого, вы можете сравнить длины двух строк.

Чтобы избежать всех этих сложностей, вы можете использовать словарь вместо списка. Их ключ будет строчной строкой, а значение будет реальной строкой. Таким образом, производительность не пострадает, потому что вам не нужно использовать ToLower для каждого сравнения, но вы все равно можете использовать Contains.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...