Проверить, содержит ли строка элемент из списка (строк)

Для следующего блока кода:

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

Результат:

Случай 1:

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

Случай 2:

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

Список (listOfStrings) может содержать несколько элементов (минимум 20), и его нужно сравнивать с тысячами строк (например, myString).

Есть ли лучший (более эффективный) способ написать этот код?


person user57175    schedule 01.02.2009    source источник


Ответы (11)


С LINQ и с использованием C # (в наши дни я мало знаю VB):

bool b = listOfStrings.Any(s=>myString.Contains(s));

или (короче и эффективнее, но, возможно, менее ясно):

bool b = listOfStrings.Any(myString.Contains);

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


update: если вы действительно имеете в виду «StartsWith», то вы можете отсортировать список и поместить его в массив; затем используйте Array.BinarySearch, чтобы найти каждый элемент - проверьте поиском, полное или частичное совпадение.

person Marc Gravell    schedule 01.02.2009
comment
Вместо Contains я бы использовал StartsWith на основе его примеров. - person tvanfosson; 01.02.2009
comment
@tvanfosson - это зависит от того, являются ли примеры полностью инклюзивными, но да, я согласен. Конечно, легко изменить. - person Marc Gravell; 01.02.2009
comment
Насколько этот код более эффективен на алгоритмическом уровне? Это короче и быстрее, если циклы в Any выполняются быстрее, но проблема, заключающаяся в том, что вам приходится выполнять точное сопоставление много раз, остается прежней. - person Torsten Marek; 01.02.2009
comment
Вы можете настроить собственный компаратор, если используете набор. - person Fortyrunner; 01.02.2009
comment
Второй вариант на самом деле не более эффективен при какой-либо ощутимой разнице на практике. - person ICR; 01.02.2009
comment
@ICR - вы не так уж много можете (легко) сделать для соответствия contains. Если он начинается с, я добавил комментарий к двоичному поиску - это будет быстрее. - person Marc Gravell; 01.02.2009
comment
Почему последний более эффективен, чем лямбда? - person ca9163d9; 26.09.2012
comment
@NickW ты имеешь в виду средний? Первый создает делегата анонимного метода в контексте захвата со ссылкой на myString, который затем вызывает метод string.Contains в поле myString. Второй просто создает делегата для string.Contains метода с myString в качестве target для делегата - гораздо более прямолинейно. - person Marc Gravell; 26.09.2012
comment
Я считаю, что использование этого метода расширения с ключевым словом params для массива делает его более читаемым. myString.ContainsAny("this", "that", "those");. - person Connell; 29.05.2015
comment
Как узнать, какое значение совпало? Когда я передал S, чтобы получить значение, я получаю ошибку сборки. - person kirushan; 07.03.2017
comment
@MarcGravell Какова временная сложность работы? - person Jumabek Alikhanov; 28.05.2017

когда вы строите свои строки, это должно быть так

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));
person Simi2525    schedule 19.12.2014

Был ряд предложений из более раннего аналогичного вопроса "Лучший способ проверить существующую строку по большому списку сопоставимых элементов".

Регулярного выражения может быть достаточно для вашего требования. Выражение будет объединением всех подстрок-кандидатов с оператором OR "|" между ними. Конечно, вам придется остерегаться неэкранированных символов при построении выражения или невозможности его скомпилировать из-за сложности или ограничений по размеру.

Другой способ сделать это - создать структуру данных trie для представления всех подстрок-кандидатов ( это может несколько дублировать то, что делает сопоставитель регулярных выражений). По мере прохождения каждого символа в тестовой строке вы должны создать новый указатель на корень дерева и продвинуть существующие указатели на соответствующий дочерний элемент (если есть). Вы получаете совпадение, когда любой указатель достигает листа.

person Zach Scrivena    schedule 01.02.2009

Мне понравился ответ Марка, но мне нужно, чтобы соответствие Contains было CaSe InSenSiTiVe.

Это было решение:

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))
person WhoIsRich    schedule 01.05.2014
comment
Разве это не должно быть ›-1? - person CSharped; 15.06.2016
comment
@CSharped Не имеет значения, поскольку ›-1 (больше минус 1) и› = 0 (больше или равно нулю) - это одно и то же. - person WhoIsRich; 15.06.2016

Старый вопрос. Но поскольку первоначальным требованием было VB.NET. Используя те же значения принятого ответа:

listOfStrings.Any(Function(s) myString.Contains(s))
person Luis Lavieri    schedule 11.07.2019

Основываясь на ваших шаблонах, можно было бы улучшить использование StartsWith вместо Contains. StartsWith нужно только перебирать каждую строку до тех пор, пока не будет найдено первое несоответствие, вместо того, чтобы перезапускать поиск в каждой позиции символа, когда он его находит.

Кроме того, на основе ваших шаблонов похоже, что вы можете извлечь первую часть пути для myString, а затем отменить сравнение - ища начальный путь myString в списке строк, а не наоборот.

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

РЕДАКТИРОВАТЬ: это было бы еще быстрее, используя идею HashSet, о которой упоминает @Marc Gravell, поскольку вы можете изменить Contains на ContainsKey, и поиск будет O (1) вместо O (N). Вы должны убедиться, что пути точно совпадают. Обратите внимание, что это не общее решение, как у @Marc Gravell, но оно адаптировано к вашим примерам.

Извините за пример C #. Мне не хватило кофе, чтобы перевести на VB.

person tvanfosson    schedule 01.02.2009
comment
Повторно начинается с; возможно предварительно отсортировать и использовать бинарный поиск? Это может быть снова быстрее. - person Marc Gravell; 01.02.2009

Вы проверили скорость?

т.е. вы создали образец набора данных и профилировали его? Это может быть не так плохо, как вы думаете.

Это также может быть что-то, что вы можете создать в отдельном потоке и создать иллюзию скорости!

person Fortyrunner    schedule 01.02.2009

Если скорость имеет решающее значение, вы можете поискать наборы с помощью алгоритма Ахо-Корасика. шаблонов.

Это trie с ошибочными ссылками, то есть сложность O (n + m + k) , где n - длина входящего текста, m - совокупная длина шаблонов, а k - количество совпадений. Вам просто нужно изменить алгоритм, чтобы он прекратил работу после обнаружения первого совпадения.

person Torsten Marek    schedule 01.02.2009

Недостатком метода Contains является то, что он не позволяет указывать тип сравнения, что часто важно при сравнении строк. Он всегда чувствителен к культуре и регистру. Поэтому я думаю, что ответ WhoIsRich ценен, я просто хочу показать более простую альтернативу:

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
person Al Kepp    schedule 05.11.2018

Поскольку мне нужно было проверить, есть ли элементы из списка в (длинной) строке, я остановился на этом:

listOfStrings.Any(x => myString.ToUpper().Contains(x.ToUpper()));

Или в vb.net:

listOfStrings.Any(Function(x) myString.ToUpper().Contains(x.ToUpper()))
person LiliumCandidum    schedule 15.10.2020

Небольшое изменение, мне нужно было выяснить, есть ли в строке целые слова и нечувствительность к регистру.

myString.Split(' ', StringSplitOptions.RemoveEmptyEntries).Intersect(listOfStrings).Any())

для нечувствительности к регистру myString и listOfStrings были преобразованы в верхний регистр.

person Kevin Wolsey    schedule 18.05.2021