Сделать HashSet <string> без учета регистра

У меня есть метод с параметром HashSet. И мне нужно делать без учета регистра Содержит внутри него:

public void DoSomething(HashSet<string> set, string item)
{
 var x = set.Contains(item);
 ... 
}
</string>

Можно ли сделать существующий HashSet без учета регистра (не создавать новый)?

Я ищу решение с наилучшими характеристиками.

Edit

Содержит многократные вызовы. Поэтому расширения IEnumerable не приемлемы для меня из-за более низкой производительности, чем собственный метод HashSet Contains.

Решение

Так как ответ на мой вопрос НЕТ, это невозможно, я создал и использовал следующий метод:

public HashSet<string> EnsureCaseInsensitive(HashSet<string> set)
{
 return set.Comparer == StringComparer.OrdinalIgnoreCase
 ? set
 : new HashSet<string>(set, StringComparer.OrdinalIgnoreCase);
}
</string></string></string>
6 ответов

Конструктор HashSet имеет перегрузку, которая позволяет передавать пользовательский IEqualityComparer. Некоторые из них определены для вас уже в статическом классе StringComparer, некоторые из которых игнорируют регистр. Например:

var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
set.Add("john");
Debug.Assert(set.Contains("JohN"));
</string>

Вам нужно будет внести это изменение во время создания HashSet. Как только он существует, вы не можете изменить IEqualityComparer его использование.

Как вы знаете, по умолчанию (если вы не передаете какой-либо IEqualityComparer в конструктор HashSet), вместо этого он использует EqualityComparer<t>.Default</t>.

Изменить

Вопрос, похоже, изменился после того, как я отправил свой ответ. Если вам нужно сделать поиск без учета регистра в существующем чувствительном к регистру HashSet, вам придется выполнять линейный поиск:

set.Any(s => string.Equals(s, item, StringComparison.OrdinalIgnoreCase));

Ничего подобного.


Вы не можете волшебным образом сделать случайный HashSet (или словарь), чтобы вести себя без учета регистра.

Вам нужно воссоздать один из них внутри вашей функции, если вы не можете полагаться на входящий HashSet, чтобы он не зависел от регистра.

Самый компактный код - используйте конструктор из существующего набора:

var insensitive = new HashSet<string>(
 set, StringComparison.InvariantCultureIgnoreCase);
</string>

Обратите внимание, что копирование HashSet столь же дорого, как и прохождение всех элементов, поэтому, если ваша функция выполняет только поиск, было бы дешевле (O (n)) перебирать все элементы. Если ваша функция вызывается несколько раз, чтобы сделать один нечувствительный к регистру поиск, вы должны попытаться передать ему HashSet вместо этого.


HashSet предназначен для быстрого поиска элементов в соответствии с его хэширующей функцией и компаратором равенства. То, о чем вы просите, действительно найти элемент, соответствующий "некоторым другим" условиям. Представьте, что у вас есть объекты Set, которые используют только Person.Name для сравнения, и вам нужно найти элемент с некоторым заданным значением Person.Age.

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

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


Предполагая, что у вас есть этот метод расширения:

public static HashSet<t> ToHashSet<t>(this IEnumerable<t> source)
{
 return new HashSet<t>(source);
}
</t></t></t></t>

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

set = set.Select(n => n.ToLowerInvariant()).ToHashSet();

Или вы могли бы просто сделать это:

set = new HashSet(set, StringComparer.OrdinalIgnoreCase); 
//or InvariantCultureIgnoreCase or CurrentCultureIgnoreCase


Конструктор HashSet может принять альтернативу IEqualityComparer, которая может переопределить, как определяется равенство. См. Список конструкторов здесь.

Класс StringComparer содержит кучу статических экземпляров IEqualityComparers для строк. В частности, вас, возможно, интересует StringComparer.OrdinalIgnoreCase. Здесь - документация StringComparer.

Обратите внимание, что другой конструктор принимает IEnumerable, поэтому вы можете построить новый HashSet из своего старого, но с IEqualityComparer.

Итак, все вместе, вы хотите преобразовать HashSet следующим образом:

var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);


Если вы хотите оставить оригинальную версию с учетом регистра, вы можете просто запросить ее с помощью linq с нечувствительностью к регистру:

var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));

licensed under cc by-sa 3.0 with attribution.