2016-02-28 6 views
6

Ich habe 2 Liste welche Namen ListA und ListB sind.Wie können Sie Strings in einer Liste aus einer anderen Liste entfernen?

möchte ich Strings in ListeB entfernen, die in listA sind, aber ich möchte dies auf diese Weise tun:

wenn listA enthält: "bar", "bar", "bar", "foo" und ListeB enthält: "bar"

es entfernt nur 1 bar und das Ergebnis wird sein: "bar", "bar", "foo"

der Code, den ich schrieb entfernt alle "bar":

List<string> result = listA.Except(listB).ToList(); 
+0

Hat einige der ursprünglichen Liste der Reihenfolge Materie Halte? – hatchet

Antwort

5

Sie können versuchen, es eins nach dem anderen zu entfernen:

foreach (var word in listB) 
    listA.Remove(word); 

Die Remove-Methode ein Element nur zu einem Zeitpunkt entfernen wird und nicht Ausnahme werfen (aber Rückkehr false), wenn das Element nicht gefunden wird: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx

+0

Sie können den Aufruf von Contains vermeiden, indem Sie direkt IndexOf verwenden, um die Position – Steve

+1

zu erhalten. Es ist ineffizient, aber Sie können es zumindest verwenden, um 'listA.Remove (word)' direkt zu verwenden. Keine Notwendigkeit von 'Enthält'. –

+0

@IvanStoev Recht, nur aktualisieren Sie es – Ian

3
var listA = new List<string>() { "bar", "bar", "bar", "foo" }; 
var listB = new List<string>() { "bar" }; 

foreach (var word in listB){ 
    listA.Remove(word); 
} 
+0

Dies ist im Grunde eine Kopie der @Ian Antwort geschrieben 10+ Minuten vor Ihrem –

+0

ja, es ist wahr, ich upvoted seine Antwort – csa

1

Dies ist eine schnellere Methode, aber es ist wahrscheinlich, die Reihenfolge der Elemente der ersten Liste zu ändern. Steps:

  • Karte die Lista zu einem Dictionary<string, int> (lass es listAMap nennen), wo Schlüssel ist das Element der Liste und Wert ist die Gesamtzahl der für diesen Wert in listA aufgetreten ist;
  • Iterieren durch listB und für jedes Element von listB, wenn dieses Element in der listAMap ist, reduzieren Sie die Anzahl;
  • Holen Sie sich die Schlüssel von listMapA mit Keys property von C# -Dokumenten, und durchlaufen Sie alle Schlüssel. Für jeden Schlüssel, der einen positiven Wert hat, addiere diesen Schlüssel zu einer anderen Liste insgesamt seine Zählzeiten. Wenn also ein Eintrag "bar" -> 2 ist, dann fügen Sie zweimal "bar" in die neue Liste ein.

Gesamtlaufzeit des Algorithmus ist O (m + n), wobei m und n die Anzahl der Elemente in den beiden ursprünglichen Listen. Es ist eine bessere Laufzeit als andere Ansätze hier erwähnt, die O (m * n) Laufzeit haben. Offensichtlich benötigt dieser Algorithmus mehr Platz.


Supportive-Code für den Algorithmus oben:

//Step-1: Create the dictionary... 
var listAMap = new Dictionary<string, int>(); 
foreach (var listAElement in listA) 
{ 
    listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1); 
} 

// Step-2: Remove the listB elements from dictionary... 
foreach (var listBElement in listB) 
{ 
    if (listAMap.Contains(listBElement)) listAMap[listBElement]--; 
} 

//Step-3: Create the new list from pruned dictionary... 
var prunedListA = new List<string>(); 
foreach (var key in listAMap.Keys) 
{ 
    if (listAMap[key] <= 0) continue; 
    for (var count = 0; count < listAMap[key]; count++) 
    { 
     prunedListA.Add(key); 
    } 
} 

//prunedListA contains the desired elements now. 
+0

Ich dachte für etwas Ähnliches, aber listB zählen und dann entfernen Die Elemente von listA tah stimmen überein (und verringern die Übereinstimmungsanzahl). Anyway, +1 für das Nachdenken über Effizienz. –

+0

@IvanStoev: Wir stimmen nicht mit den Elementen in der Liste überein.Wir machen eine O (1) Suche in einem Wörterbuch. Im Ernst, die Lösung ist sehr einfach (Nicht, dass Sie eine +2 auf die Antwort geben können). Ich hätte den Code auch hinzufügen sollen. Will tun, wenn ich vom Laptop auf SO zugreife. – displayName

+0

@IvanStoev: Das letzte, was jetzt getan werden muss, ist, dass der obige Code in eine separate Methode einbezogen wird, damit er sauberer ist. – displayName

1

Hier ist ein effizienter Weg, dies zu tun:

var countB = new Dictionary<string, int>(listB.Count); 
foreach (var x in listB) 
{ 
    int count; 
    countB.TryGetValue(x, out count); 
    countB[x] = count + 1; 
} 
listA.RemoveAll(x => 
{ 
    int count; 
    if (!countB.TryGetValue(x, out count)) return false; 
    if (count == 1) 
     countB.Remove(x); 
    else 
     countB[x] = count - 1; 
    return true; 
}); 
+0

Sie haben den Schritt verpasst, in dem Sie * countB * ausfüllen. – displayName

+0

@displayName Ich habe nicht - versuchen und sehen (Hinweis - die kleine Zeile 'countB [x] = Anzahl + 1;') :) –

+0

Oh ich verstehe ... wusste nicht über dieses Verhalten von 'TryGetValue()' in Wörterbüchern. Etwas Neues gelernt. – displayName