2013-06-04 10 views
36

Ich habe Methode mit HashSet-Parameter. Und ich tun muß Groß- und Kleinschreibung in ihrem Inhalt:Make HashSet <string> case-insensitive

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

Ist es eine Möglichkeit, bestehende HashSet Groß- und Kleinschreibung zu machen (nicht schaffen neue)?

Ich suche eine Lösung mit bester Leistung.

bearbeiten

enthält, kann mehrmals aufgerufen werden. IEnumerable-Erweiterungen sind daher aufgrund geringerer Leistung als die native HashSet Contains-Methode für mich nicht akzeptabel.

Lösung

Da Antwort auf meine Frage ist nicht, es ist unmöglich, habe ich erstellt und verwendet folgende Methode:

public HashSet<string> EnsureCaseInsensitive(HashSet<string> set) 
{ 
    return set.Comparer == StringComparer.OrdinalIgnoreCase 
      ? set 
      : new HashSet<string>(set, StringComparer.OrdinalIgnoreCase); 
} 
+5

Sie müssen wahrscheinlich eine neue erstellen ... –

+0

Mögliche Duplikat: http://StackOverflow.com/Questions/2667635/How-to-use-hashsetstring-contains-method-in-case-insensitive- mode (s. user414076's answer) –

+0

Sie müssen sich im Voraus entscheiden, ob der 'HashSet' Groß- und Kleinschreibung berücksichtigt, indem Sie einen Vergleicher liefern. Es ist jedoch eine Überlegung wert, dass die Menge {"A", "a"} nur ein Element enthält, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird. – spender

Antwort

67

Der HashSet<T> Konstruktor eine Überlastung, die Sie in einem benutzerdefinierten IEqualityComparer<string> passieren lässt. Es gibt ein paar von diesen definiert für Sie bereits in der statischen StringComparer Klasse, von denen einige Groß- und Kleinschreibung ignorieren. Zum Beispiel:

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

Sie werden diese Änderung zu der Zeit machen müssen, um die HashSet<T> zu konstruieren. Sobald einer existiert, können Sie die verwendete IEqualityComparer<T> nicht ändern.


Nur damit Sie wissen, die standardmäßig (wenn Sie sich nicht in jedem IEqualityComparer<T> zum HashSet<T> Konstruktor), verwendet es EqualityComparer<T>.Default statt.


bearbeiten

Die scheint Frage geändert zu haben, nachdem ich meine Antwort geschrieben. Wenn Sie einen Fall unempfindlich Suche in einem bestehenden Fall empfindlichHashSet<string> zu tun haben, werden Sie eine lineare Suche zu tun haben:

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

Sich um dieses keine Möglichkeit.

+0

Wenn Sie eine einzelne Suche - das ist schlimmer als nur über das Hash-Set Looping –

+0

@DaveBish Ich glaube, der OP änderte seine Frage zu sagen, "nicht neue erstellen", nachdem ich geantwortet hatte ... (Änderungen sehr bald nach dem Posten zählen nicht als Änderungen). - Wenn das OP dies mit * einem vorhandenen * 'HashSet ' machen muss, dann wird er natürlich eine lineare Zeitsuche durchführen müssen. –

+1

Das sage ich nicht. Wenn er nur eine Suche nach dem Hashset durchführt, ist die Erstellung eines neuen Hashes teurer als ein linearer Scan. (Op nicht angegeben) –

3

Vorausgesetzt, dass Sie diese Erweiterung Methode haben:

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

Sie können dies nur verwenden:

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

Oder könnten Sie nur dies tun:

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

Wenn Sie eine einzelne Suche durchführen - das ist schlimmer als nur über das Hash-Set zu schleifen –

+0

@DaveBish Warum ist das? –

+0

Es würde eine Menge Speicher aufnehmen und eine Menge Hash-Berechnungen durchführen, und dann die ganze Arbeit nach einem Nachschlagen wegwerfen. Das Schleifen über den gesamten Hash-Satz und das Vergleichen von Groß- und Kleinschreibung werden im konstanten Speicher ausgeführt und müssen keine Hashes berechnen. Beide müssen in jedem Fall die Gesamtheit von 'set' berühren. – delnan

0

Wenn Sie das Original, case-sensitive Version an ihrem Platz bleiben möchten, können Sie es einfach abfragen, mit Linq mit Groß- und Kleinschreibung:

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

Der Konstruktor von HashSet kann eine Alternative IEqualityComparer nehmen, die überschreiben kann, wie Gleichheit bestimmt wird. Siehe die Liste der Konstruktoren here. Die Klasse StringComparer enthält eine Reihe von statischen Instanzen von IEqualityComparers für Strings. Besonders interessiert Sie wahrscheinlich StringComparer.OrdinalIgnoreCase. Here ist die Dokumentation von StringComparer.

Beachten Sie, dass ein anderer Konstruktor ein IEnumerable übernimmt, also können Sie ein neues HashSet von Ihrem alten, aber mit dem IEqualityComparer konstruieren.

Also, alles zusammen, möchten Sie Ihre HashSet konvertieren wie folgt:

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

Sie können Case-sensitive HashSet (oder Dictionary) nicht auf die Groß-/Kleinschreibung reagieren.

Sie müssen einen innerhalb Ihrer Funktion neu erstellen, wenn Sie sich nicht darauf verlassen können, dass HashSet die Groß-/Kleinschreibung nicht berücksichtigt.

Die meisten kompakten Code - verwenden constructor aus bestehenden Set:

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

Beachten Sie, dass HashSet Kopieren als zu Fuß durch alle Elemente so teuer ist, also, wenn Ihre Funktion nur auf die Suche macht es wäre billiger sein (O (n)) um alle Elemente zu durchlaufen. Wenn Ihre Funktion mehrmals aufgerufen wird, um eine Suche ohne Berücksichtigung der Groß- und Kleinschreibung durchzuführen, sollten Sie stattdessen versuchen, die richtige HashSet an sie zu übergeben.

+0

+1 für Leistungshinweise – wishmaster

4

Die HashSet wurde entwickelt, um schnell Elemente zu finden, die ihrer Hashing-Funktion und ihrem Gleichheitskomparator entsprechen. Was Sie suchen, ist wirklich, ein Element zu finden, das zu "irgendeiner anderen" Bedingung passt. Stellen Sie sich vor, Sie haben eine Set<Person> Objekte, die nur Person.Name zum Vergleich verwendet und Sie müssen ein Element mit einem bestimmten Wert von Person.Age finden.

Der Punkt ist, müssen Sie über den Inhalt des Satzes durchlaufen, um die passenden Elemente zu finden. Wenn Sie dies häufig tun, können Sie ein anderes Set erstellen, in dem Fall, dass Sie einen Komparator verwenden, der nicht zwischen Groß- und Kleinschreibung unterscheidet, aber dann müssen Sie sicherstellen, dass dieser Shadow-Satz mit dem Original übereinstimmt.

Die Antworten sind im Wesentlichen Variationen der oben genannten, ich dachte, dies hinzuzufügen, um das grundlegende Problem zu klären.