Поиск по регистру без учета регистра

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

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

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

Ответ 1

Вместо 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");

Ответ 2

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

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

Это доступно с .net 2.0 в соответствии с msdn.

Ответ 3

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

Ответ 6

На основании ответа Лэнса Ларсена - здесь используется метод расширения с рекомендуемой строкой. Компиляция вместо строки. Эквалайзер

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