Зрабіць HashSet <радок> не адчувальныя да рэгістра

У мяне ёсць метад з параметрам HashSet. І мне трэба зрабіць без уліку рэгістра ўтрымлівае ў сабе:

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

Ці ёсць які-небудзь спосаб зрабіць існуючы HashSet неадчувальны да рэгістра (не ствараць новы)?

Я шукаю рашэнне з лепшай Perfomance.

<�Моцны> Edit

Змяшчае можна назваць некалькі разоў. Так IEnumerable пашырэнне не прымальна для мяне з-за больш нізкі, чым Perfomance роднага HashSet Змяшчае метад.

<�Моцны> Рашэнне

Так, адказ на маё пытанне не, гэта немагчыма, я стварыў і выкарыстаў наступны метад:

public HashSet EnsureCaseInsensitive(HashSet set)
{
    return set.Comparer == StringComparer.OrdinalIgnoreCase
           ? set
           : new HashSet(set, StringComparer.OrdinalIgnoreCase);
}
42
Вы, верагодна, прыйдзецца стварыць новы ...
дададзена аўтар It'sNotALie., крыніца
Вы павінны вырашыць, наперадзе Ці ці не HashSet разглядае справу шляхам падачы кампаратар. Тым не менш, варта ўлічыць, што мноства { «A», «а»} будзе ўтрымліваць толькі адзін элемент з регистронезависимом кампаратара.
дададзена аўтар spender, крыніца
Магчымы дублікат: stackoverflow.com/questions/2667635/… (гл адказ user414076 в)
дададзена аўтар Esoteric Screen Name, крыніца

6 адказы

The HashSet constructor has an overload that lets you pass in a custom IEqualityComparer. There are a few of these defined for you already in the static StringComparer class, a few of which ignore case. For example:

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

You'll have to make this change at the time of constructing the HashSet. Once one exists, you can't change the IEqualityComparer it's using.


Just so you know, by default (if you don't pass in any IEqualityComparer to the HashSet constructor), it uses EqualityComparer.Default instead.


рэдагаваць

The question appears to have changed after I posted my answer. If you have to do a case insensitive search in an existing case sensitive HashSet, you will have to do a linear search:

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

Там няма ніякага спосабу абыйсці гэта.

75
дададзена
@DaveBish Я лічу, што ОП змяніў сваё пытанне, каб сказаць "не стварыць новую» пасля таго, як я адказаў ... (рэдагуе вельмі хутка пасля публікацыі фактычна не лічацца праўкамі). - Калі ОП павінен зрабіць гэта з дапамогай <�я> існуючы HashSet , то, вядома, ён павінен будзе зрабіць лінейны пошук па часе.
дададзена аўтар Timothy Shields, крыніца
@DaveBish Менавіта таму я адрэдагаваў мой адказ ўключаць лінейную разгортку LINQ. :)
дададзена аўтар Timothy Shields, крыніца
Калі вы робіце адзін пошук - гэта горш, чым проста цыкл па HashSet
дададзена аўтар Dave Bish, крыніца
Гэта не тое, што я кажу. Калі ён толькі робіць адзін пошук супраць HashSet - стварэнне новага з'яўляецца больш дарагім, чым лінейнае сканаванне. (Op не ўдакладніў)
дададзена аўтар Dave Bish, крыніца

Вы не можаце чароўна зрабіць выпадак-HashSet (адчувальнымі або слоўнік), каб паводзіць сябе неадчувальным да рэгістра спосабам.

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

Самы кампактны код - выкарыстоўваць канструктар з існага набору:

var insensitive = new HashSet(
   set, StringComparer.InvariantCultureIgnoreCase);

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

5
дададзена
+1 для нататак прадукцыйнасці
дададзена аўтар wishmaster, крыніца

The HashSet is designed to quickly find elements as per its hashing function and equality comparator. What you are asking for is really to find an element matching "some other" condition. Imagine that you have a Set objects that uses only Person.Name for comparison and you need to find an element with some given value of Person.Age.

Справа ў тым, што вам трэба перабраць змесціва набору, каб знайсці адпаведныя элементы. Калі вы збіраецеся рабіць гэта часта можна стварыць іншы набор, у выпадку выкарыстання вамі регистронезависимое кампаратар, але тады вы павінны пераканацца, што гэтая цень набор знаходзіцца ў сінхранізацыі з арыгіналам.

Адказы да гэтага часу, па сутнасці, варыяцыі вышэй, я думаў, каб дадаць гэта растлумачыць асноватворны пытанне.

4
дададзена

Мяркуючы, што ў вас ёсць гэты метад пашырэння:

public static HashSet ToHashSet(this IEnumerable source)
{
    return new HashSet(source);
}

Вы можаце проста выкарыстоўваць гэта:

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

Ці, вы можаце проста зрабіць гэта:

set = new HashSet(set, StringComparer.OrdinalIgnoreCase); 
//or InvariantCultureIgnoreCase or CurrentCultureIgnoreCase
3
дададзена
@DaveBish Чаму гэта?
дададзена аўтар It'sNotALie., крыніца
@DaveBish Самы upvoted адказ таксама бывае, каб зрабіць гэта ... гэта трэба рэканструяваць, а таксама ...
дададзена аўтар It'sNotALie., крыніца
@delnan Паглядзіце вышэй.
дададзена аўтар It'sNotALie., крыніца
@DaveBish Добры пытанне, лол.
дададзена аўтар It'sNotALie., крыніца
Калі вы робіце адзін пошук - гэта горш, чым проста цыкл па HashSet
дададзена аўтар Dave Bish, крыніца
Таму што стварэнне новага HashSet будзе па-крайняй меры, прыйдзецца перабрацца ўсе гэта!
дададзена аўтар Dave Bish, крыніца
Я адказваў на гэты адзін, таксама :)
дададзена аўтар Dave Bish, крыніца
Было б захапіць шмат памяці і зрабіць шмат хэш-вылічэнняў, а затым кінуць усю гэтую працу далей пасля аднаго пошуку. Цыклічны па ўсім хешу-набору і рабіць параўнанне без уліку рэгістра працуе ў пастаяннай памяці і не павінна разлічыць хэш. І трэба закрануць паўнату набор у любым выпадку.
дададзена аўтар delnan, крыніца

Канструктар HashSet можа прымаць альтэрнатыўны IEqualityComparer , які можа перавызначыць як вызначаецца роўнасцю. Глядзіце спіс канструктараў .

Клас StringComparer змяшчае кучу статычных асобнікаў IEqualityComparers для радкоў. У прыватнасці, вы, верагодна, зацікаўлены ў StringComparer.OrdinalIgnoreCase . Тут з'яўляецца дакументацыя StringComparer .

Звярніце ўвагу, што іншы канструктар прымае ў IEnumerable , так што вы можаце пабудаваць новы HashSet з вашай старой, але з IEqualityComparer .

Такім чынам, усе разам, вы хочаце, каб пераўтварыць ваш HashSet наступным чынам:

var myNewHashSet = new HashSet(myOldHashSet, StringComparer.OrdinalIgnoreCase);
2
дададзена

Калі вы жадаеце пакінуць арыгінальную, адчувальныя да рэгістра версіі на месцы, вы можаце проста запытаць яго з дапамогай LINQ з неадчувальныя да рэгістра:

var contains = set.Any(a => a.Equals(item, StringComparison.InvariantCultureIgnoreCase));
0
дададзена