Listensuche ohne Berücksichtigung der Groß- und Kleinschreibung

144

Ich habe eine Liste testList, die eine Reihe von Zeichenfolgen enthält. Ich möchte testListnur dann eine neue Zeichenfolge hinzufügen, wenn diese noch nicht in der Liste vorhanden ist. Daher muss ich die Liste ohne Berücksichtigung der Groß- und Kleinschreibung durchsuchen und sie effizient gestalten. Ich kann nicht verwenden, Containsda dies das Gehäuse nicht berücksichtigt. Ich möchte auch ToUpper/ToLoweraus Leistungsgründen nicht verwenden. Ich bin auf diese Methode gestoßen, die funktioniert:

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

Dies funktioniert, stimmt aber auch mit Teilwörtern überein. Wenn die Liste "Ziege" enthält, kann ich "Hafer" nicht hinzufügen, da behauptet wird, dass "Hafer" bereits in der Liste enthalten ist. Gibt es eine Möglichkeit, Listen effizient ohne Berücksichtigung der Groß- und Kleinschreibung zu durchsuchen, bei denen Wörter genau übereinstimmen müssen? Vielen Dank

Brap
quelle

Antworten:

180

Verwenden Sie anstelle von String.IndexOf String.Equals, um sicherzustellen , dass Sie keine Teilübereinstimmungen haben. Verwenden Sie FindAll auch nicht, da dies jedes Element durchläuft. Verwenden Sie FindIndex (es stoppt beim ersten Treffer).

if(testList.FindIndex(x => x.Equals(keyword,  
    StringComparison.OrdinalIgnoreCase) ) != -1) 
    Console.WriteLine("Found in list"); 

Verwenden Sie alternativ einige LINQ-Methoden (die auch beim ersten Treffer anhalten)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");
Adam Sills
quelle
In einigen kurzen Tests scheint die erste Methode etwa 50% schneller zu sein. Vielleicht kann jemand anderes das bestätigen / leugnen.
Brap
8
Ab .NET 2.0 ist dies jetzt ganz einfach - sehen Sie sich Shaxbys Antwort unten an.
Joe
3
Die Contains-Methode shaxby's Referenzierung (die eine Überladung hat, die einen IEqualityComparer erfordert) ist Teil von LINQ, daher ist sie seit .NET 2.0 sicherlich nicht mehr verfügbar. Nur die StringComparer-Klasse gibt es schon eine Weile. List <T> hat weder diese Methode noch ArrayList oder StringCollection (Dinge, die er leicht als seine 'Liste' hätte bezeichnen können).
Adam Sills
Nun, da ich den Index tatsächlich brauchte , war dies definitiv die beste Antwort für mich.
Nyerguds
1
Die erste Lösung sollte die List<>.Exists(Predicate<>)Instanzmethode verwenden. Beachten Sie auch, dass nulldies explodieren kann , wenn die Liste Einträge enthält . In diesem Fall ist es sicherer zu sagen keyword.Equals(x, StringComparison.OrdinalIgnoreCase)als x.Equals(keyword, StringComparison.OrdinalIgnoreCase)(wenn Sie garantieren können, dass das keywordniemals null ist).
Jeppe Stig Nielsen
359

Mir ist klar, dass dies ein alter Beitrag ist, aber nur für den Fall, dass jemand anderes sucht, können Sie ihn verwenden, Containsindem Sie den Vergleich zwischen Zeichenfolgengleichheit ohne Berücksichtigung der Groß- und Kleinschreibung wie folgt bereitstellen:

using System.Linq;

// ...

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

Dies ist seit .net 2.0 laut msdn verfügbar .

shaxby
quelle
21
Auf jeden Fall die beste Antwort hier draußen. :)
Joe
22
Enumerable <T> .Contains (worauf Sie verweisen) gibt es seit .NET 2.0 nicht mehr. Es gibt keine Liste <T>. Enthält die Überladung, die Sie verwenden.
Adam Sills
@ AdamSills richtig. Es gibt keine solche Methode in Liste <T>. Und wenn es sich um eine faule Sammlung handelt, kann sie ein paar Mal wiederholt werden, wie dies bei anderen Enumerable <T> -Methoden der Fall ist. Imho, diese Methode sollte in solchen Fällen nicht verwendet werden, da sie für diesen Fall nicht so logisch ist.
Sergey Litvinov
40
Ich habe diese Überlastung zunächst auch nicht gesehen, aber Sie müssen sie mit System.Linq hinzufügen, dann wird sie angezeigt.
Michael
1
Die StringComparerKlasse gibt es seit 2.0, aber diese Überladung von Contains wurde in 3.5 eingeführt. msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx
Denise Skidmore
18

Basierend auf der obigen Antwort von Adam Sills - hier ist eine schöne saubere Erweiterungsmethode für Enthält ... :)

///----------------------------------------------------------------------
/// <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);
}
Lance Larsen - Microsoft MVP
quelle
10

Sie können StringComparer verwenden:

    var list = new List<string>();
    list.Add("cat");
    list.Add("dog");
    list.Add("moth");

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    {
        Console.WriteLine("found");
    }
jlo-gmail
quelle
1
Solange Sie "using System.Linq" hinzufügen, wird diese Überlastung für .Contains nicht angezeigt.
Julian Melville
1

Basierend auf der Antwort von Lance Larsen - hier ist eine Erweiterungsmethode mit der empfohlenen Zeichenfolge. Vergleichen anstelle von Zeichenfolge. Gleichungen

Es wird dringend empfohlen, eine Überladung von String.Compare zu verwenden, die einen StringComparison-Parameter verwendet. Mit diesen Überladungen können Sie nicht nur das genaue Vergleichsverhalten definieren, das Sie beabsichtigt haben, sondern auch Ihren Code für andere Entwickler lesbarer machen. [ 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);
}
dontbyteme
quelle
0

Sie überprüfen, ob das Ergebnis von IndexOf größer oder gleich 0 ist. Dies bedeutet, ob die Übereinstimmung an einer beliebigen Stelle in der Zeichenfolge beginnt . Versuchen Sie zu überprüfen, ob es gleich 0 ist:

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

Jetzt passen "Ziege" und "Hafer" nicht zusammen, aber "Ziege" und "Ziege". Um dies zu vermeiden, können Sie die Längen der beiden Zeichenfolgen vergleichen.

Um all diese Komplikationen zu vermeiden, können Sie anstelle einer Liste ein Wörterbuch verwenden. Der Schlüssel wäre die Kleinbuchstabenzeichenfolge, und der Wert wäre die echte Zeichenfolge. Auf diese Weise wird die Leistung nicht beeinträchtigt, da Sie nicht ToLowerfür jeden Vergleich verwenden müssen, aber dennoch verwenden können Contains.

Ilya Kogan
quelle
0

Unten finden Sie ein Beispiel für die Suche nach einem Schlüsselwort in der gesamten Liste und das Entfernen dieses Elements:

public class Book
{
  public int BookId { get; set; }
  public DateTime CreatedDate { get; set; }
  public string Text { get; set; }
  public string Autor { get; set; }
  public string Source { get; set; }
}

Wenn Sie ein Buch entfernen möchten, das ein Schlüsselwort in der Text-Eigenschaft enthält, können Sie eine Liste von Schlüsselwörtern erstellen und aus der Liste der Bücher entfernen:

List<Book> listToSearch = new List<Book>()
   {
        new Book(){
            BookId = 1,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = " test voprivreda...",
            Autor = "abc",
            Source = "SSSS"

        },
        new Book(){
            BookId = 2,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = "here you go...",
            Autor = "bcd",
            Source = "SSSS"


        }
    };

var blackList = new List<string>()
            {
                "test", "b"
            }; 

foreach (var itemtoremove in blackList)
    {
        listToSearch.RemoveAll(p => p.Source.ToLower().Contains(itemtoremove.ToLower()) || p.Source.ToLower().Contains(itemtoremove.ToLower()));
    }


return listToSearch.ToList();
Himanshu Chopra
quelle
-1

Ich hatte ein ähnliches Problem, ich brauchte den Index des Artikels, aber es musste die Groß- und Kleinschreibung nicht berücksichtigt werden. Ich habe mich ein paar Minuten im Internet umgesehen und nichts gefunden. Deshalb habe ich nur eine kleine Methode geschrieben, um dies zu erreichen tat:

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

Fügen Sie diesen Code derselben Datei hinzu und nennen Sie ihn folgendermaßen:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

Hoffe das hilft, viel Glück!

Affe im Pyjama
quelle
1
Warum eine zweite Liste erstellen? Das ist nicht sehr effizient. für (var i = 0; i <itemsList.Count; i ++) {if (item.ToLower () == searchItem.ToLower ()) {return i}}
wesm
Ich denke, wir werden es nie erfahren.
Denny