So entfernen Sie leere Zeichenfolgen aus der Liste und anschließend doppelte Werte aus einer Liste

82

Nehmen wir an, ich habe eine Liste einiger Spaltenwerte aus einer Tabelle. Wie entferne ich leere Zeichenfolgen und doppelte Werte? Bitte beachten Sie den folgenden Code:

List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();

Dies ist, was ich gerade codiert habe, aber Amirams Code ist viel eleganter, also werde ich diese Antwort hier so wählen, wie ich es gemacht habe:

DataTable dtReportsList = someclass.GetReportsList();

        if (dtReportsList.Rows.Count > 0)
       { 
           List<string> dtList = dtReportsList.AsEnumerable().Select(dr => dr.Field<string>("column1")).ToList();
           dtList.RemoveAll(x=>x == "");
           dtList = dtList.Distinct().ToList();         

           rcboModule.DataSource = dtList;
           rcboModule.DataBind();               
           rcboModule.Items.Insert(0, new RadComboBoxItem("All", "All"));
       }
Entwickler
quelle
Verstehe, dass RemoveAll () dtList mutiert; Jedes entfernte Element zwingt die Liste, Elemente in höheren Indizes im zugrunde liegenden Array neu anzuordnen. Es wäre schneller, sie einfach zu überspringen, wie es Amiram mit seiner Where-Methode tut.
KeithS

Antworten:

201
dtList  = dtList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList()

Ich nahm an, dass leere Zeichenfolgen und Leerzeichen wie null sind. Wenn nicht, können Sie IsNullOrEmpty(Leerzeichen zulassen) oder verwendens != null

Amiram Korach
quelle
Nur eine Sache; Das Dedupieren mit Distinct () ist relativ ineffizient, da die Methode den schlimmsten Fall annehmen muss.
KeithS
@KeithS Welche Aussagen kennen wir über diese Daten, die Distinctes nicht ermöglichen, sie zu optimieren?
Servy
Wir können die Liste sortieren und dann behaupten, dass sie sortiert ist, wodurch der Dedupierungsalgorithmus linear wird. siehe meine Antwort.
KeithS
9

Amirams Antwort ist richtig, aber Distinct () wie implementiert ist ein N. 2 -Operation; Für jedes Element in der Liste vergleicht der Algorithmus es mit allen bereits verarbeiteten Elementen und gibt es zurück, wenn es eindeutig ist, oder ignoriert es, wenn nicht. Wir können es besser machen.

Eine sortierte Liste kann in linearer Zeit dedupiert werden. Wenn das aktuelle Element dem vorherigen Element entspricht, ignorieren Sie es, andernfalls geben Sie es zurück. Das Sortieren ist NlogN. Selbst wenn wir die Sammlung sortieren müssen, erhalten wir einige Vorteile:

public static IEnumerable<T> SortAndDedupe<T>(this IEnumerable<T> input)
{
   var toDedupe = input.OrderBy(x=>x);

   T prev;
   foreach(var element in toDedupe)
   {
      if(element == prev) continue;

      yield return element;
      prev = element;      
   }
}

//Usage
dtList  = dtList.Where(s => !string.IsNullOrWhitespace(s)).SortAndDedupe().ToList();

Dies gibt die gleichen Elemente zurück; Sie sind nur sortiert.

KeithS
quelle
Großartig. Wenn ich mich nicht irre, führen Sie durch Iterieren der Elemente die Bestellung tatsächlich aus. Können Sie sich einen Weg vorstellen, Ihre Methode "faul" zu machen?
Amiram Korach
Leider erfordern die meisten Sorten Kenntnisse über die gesamte Sammlung, um sortiert zu werden. Das allerletzte Element könnte das erste sein, das zurückgegeben werden muss. Daher müssen alle Elemente der Eingabe ausgewertet werden, um das erste Element der Ausgabe zu erzeugen. Die einzige Sortierung, die mir einfällt, könnte unterbrochen werden, nachdem das nächste Element der Ausgabe gefunden wurde, ist eine SelectionSort-Variante. In diesem Fall sind wir wieder da, wo wir begonnen haben.
KeithS
Außerdem ist in unserem Fall das Ergebnis der gesamten Operation eine Liste, die zunächst eine "eifrige" Ausführung erfordert. Wenn wir damit als IEnumerable arbeiten und die Ausführung verzögern möchten, können Sie das Fleisch der Funktion in eine versteckte Iterator-Klasse einfügen, die IEnumerable implementiert.
KeithS
Distinctverwendet Hashing und sollte näher an O (N) als an O (N ^ 2) liegen. Quelle
Riskanter Martin
... Nun, ich werde verdammt sein, das tut es tatsächlich. System.Linq.Set ist eine von Distinct verwendete interne Hashtabellenimplementierung, die nahe der O (1) -Zugriffszeit liegt, vorausgesetzt, die GetHashCode () -Implementierung Ihrer Elemente ist effizient und erzeugt einen gleichmäßig verteilten Hash (die Standardimplementierung würde dies tun). . Eine Hashtabelle weist jedoch Speicherprobleme auf. Die Basisimplementierung von .NET verwendet zwei Arrays, eines von Ints und eines von verknüpften Elementen, von denen jedes bestenfalls der Anzahl der Elemente in der Gruppe entspricht und im schlimmsten Fall doppelt so hoch ist.
KeithS
1

Amiram Korach Lösung ist in der Tat ordentlich. Hier ist eine Alternative aus Gründen der Vielseitigkeit.

var count = dtList.Count;
// Perform a reverse tracking.
for (var i = count - 1; i > -1; i--)
{
    if (dtList[i]==string.Empty) dtList.RemoveAt(i);
}
// Keep only the unique list items.
dtList = dtList.Distinct().ToList();
Ich brauche Hilfe
quelle
4
Während dies funktionieren würde, ist die Where-Klausel schneller, da die Eingabesammlung nicht mutiert werden muss. Sie minimieren die Anzahl der "Verschiebungen", die ausgeführt werden müssen, wenn Elemente aus der Liste entfernt werden, wobei Where jedoch nichts aus der Eingabe entfernt. Es werden nur Elemente übersprungen, die nicht übereinstimmen.
KeithS
0

Um die Lösung von Amiram Korach zu vereinfachen :

dtList.RemoveAll(s => string.IsNullOrWhiteSpace(s))

Sie müssen weder Distinct () noch ToList () verwenden.

Bojan
quelle