Angenommen, ich habe eine LINQ-Abfrage wie:
var authors = from x in authorsList
where x.firstname == "Bob"
select x;
Wie kann ich angesichts der Tatsache, dass authorsList
vom Typ List<Author>
ist, die Author
-Elemente aus authorsList
löschen, die von der Abfrage in authors
zurückgegeben werden?
Oder anders ausgedrückt, wie kann ich alle gleichwertigen Bob des Vornamens aus authorsList
löschen?
Hinweis: Dies ist ein vereinfachtes Beispiel für die Frage.
Nun, es wäre einfacher, sie von vornherein auszuschließen:
authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList();
Dies würde jedoch nur den Wert von authorsList
ändern, anstatt die Autoren aus der vorherigen Sammlung zu entfernen. Alternativ können Sie RemoveAll
verwenden:
authorsList.RemoveAll(x => x.FirstName == "Bob");
Wenn Sie es wirklich tun müssen, basierend auf einer anderen Sammlung, würde ich HashSet, RemoveAll und Contains verwenden:
var setToRemove = new HashSet<Author>(authors);
authorsList.RemoveAll(x => setToRemove.Contains(x));
Um dies zu erreichen, ist es besser, List <T> .RemoveAll zu verwenden.
authorsList.RemoveAll((x) => x.firstname == "Bob");
Wenn Sie wirklich Elemente entfernen müssen, was ist dann mit Except ()?
Sie können die Liste basierend auf einer neuen Liste entfernen oder durch Schachteln des Linq-Objekts entfernen.
var authorsList = new List<Author>()
{
new Author{ Firstname = "Bob", Lastname = "Smith" },
new Author{ Firstname = "Fred", Lastname = "Jones" },
new Author{ Firstname = "Brian", Lastname = "Brains" },
new Author{ Firstname = "Billy", Lastname = "TheKid" }
};
var authors = authorsList.Where(a => a.Firstname == "Bob");
authorsList = authorsList.Except(authors).ToList();
authorsList = authorsList.Except(authorsList.Where(a=>a.Firstname=="Billy")).ToList();
Mit Standard-LINQ-Operatoren ist dies nicht möglich, da LINQ Unterstützung für Abfragen und keine Aktualisierung bietet.
Sie können jedoch eine neue Liste erstellen und die alte ersetzen.
var authorsList = GetAuthorList();
authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList();
Oder Sie können alle Elemente in authors
in einem zweiten Durchlauf entfernen.
var authorsList = GetAuthorList();
var authors = authorsList.Where(a => a.FirstName == "Bob").ToList();
foreach (var author in authors)
{
authorList.Remove(author);
}
Einfache Lösung:
static void Main()
{
List<string> myList = new List<string> { "Jason", "Bob", "Frank", "Bob" };
myList.RemoveAll(x => x == "Bob");
foreach (string s in myList)
{
//
}
}
Ich bin gewandert, wenn es Unterschiede zwischen RemoveAll und Except und den Profis der Verwendung von HashSet gibt, also habe ich einen schnellen Performance-Check durchgeführt :)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace ListRemoveTest
{
class Program
{
private static Random random = new Random( (int)DateTime.Now.Ticks );
static void Main( string[] args )
{
Console.WriteLine( "Be patient, generating data..." );
List<string> list = new List<string>();
List<string> toRemove = new List<string>();
for( int x=0; x < 1000000; x++ )
{
string randString = RandomString( random.Next( 100 ) );
list.Add( randString );
if( random.Next( 1000 ) == 0 )
toRemove.Insert( 0, randString );
}
List<string> l1 = new List<string>( list );
List<string> l2 = new List<string>( list );
List<string> l3 = new List<string>( list );
List<string> l4 = new List<string>( list );
Console.WriteLine( "Be patient, testing..." );
Stopwatch sw1 = Stopwatch.StartNew();
l1.RemoveAll( toRemove.Contains );
sw1.Stop();
Stopwatch sw2 = Stopwatch.StartNew();
l2.RemoveAll( new HashSet<string>( toRemove ).Contains );
sw2.Stop();
Stopwatch sw3 = Stopwatch.StartNew();
l3 = l3.Except( toRemove ).ToList();
sw3.Stop();
Stopwatch sw4 = Stopwatch.StartNew();
l4 = l4.Except( new HashSet<string>( toRemove ) ).ToList();
sw3.Stop();
Console.WriteLine( "L1.Len = {0}, Time taken: {1}ms", l1.Count, sw1.Elapsed.TotalMilliseconds );
Console.WriteLine( "L2.Len = {0}, Time taken: {1}ms", l1.Count, sw2.Elapsed.TotalMilliseconds );
Console.WriteLine( "L3.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );
Console.WriteLine( "L4.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );
Console.ReadKey();
}
private static string RandomString( int size )
{
StringBuilder builder = new StringBuilder();
char ch;
for( int i = 0; i < size; i++ )
{
ch = Convert.ToChar( Convert.ToInt32( Math.Floor( 26 * random.NextDouble() + 65 ) ) );
builder.Append( ch );
}
return builder.ToString();
}
}
}
Ergebnisse unten:
Be patient, generating data...
Be patient, testing...
L1.Len = 985263, Time taken: 13411.8648ms
L2.Len = 985263, Time taken: 76.4042ms
L3.Len = 985263, Time taken: 340.6933ms
L4.Len = 985263, Time taken: 340.6933ms
Wie wir sehen können, ist die beste Option in diesem Fall die Verwendung vonRemoveAll (HashSet)
Dies ist eine sehr alte Frage, aber ich habe einen wirklich einfachen Weg gefunden, dies zu tun:
authorsList = authorsList.Except(authors).ToList();
Da die Rückgabevariable authorsList
ein List<T>
ist, muss der von Except()
zurückgegebene IEnumerable<T>
in einen List<T>
umgewandelt werden.
Sie können auf zwei Arten entfernen
var output = from x in authorsList
where x.firstname != "Bob"
select x;
oder
var authors = from x in authorsList
where x.firstname == "Bob"
select x;
var output = from x in authorsList
where !authors.Contains(x)
select x;
Ich hatte das gleiche Problem. Wenn Sie eine einfache Ausgabe basierend auf Ihrer Where-Bedingung wünschen, ist die erste Lösung besser.
Angenommen, authorsToRemove
ist ein IEnumerable<T>
, der die Elemente enthält, die Sie aus authorsList
entfernen möchten.
Dann ist eine weitere sehr einfache Möglichkeit, die vom OP angeforderte Entfernungsaufgabe auszuführen:
authorsList.RemoveAll(authorsToRemove.Contains);
Ich denke, du könntest so etwas tun
authorsList = (from a in authorsList
where !authors.Contains(a)
select a).ToList();
Obwohl ich denke, dass die bereits gegebenen Lösungen das Problem lesbarer lösen.
Unten ist das Beispiel, um das Element aus der Liste zu entfernen.
List<int> items = new List<int>() { 2, 2, 3, 4, 2, 7, 3,3,3};
var result = items.Remove(2);//Remove the first ocurence of matched elements and returns boolean value
var result1 = items.RemoveAll(lst => lst == 3);// Remove all the matched elements and returns count of removed element
items.RemoveAt(3);//Removes the elements at the specified index
ich denke, Sie müssen nur die Elemente aus der Autorenliste einer neuen Liste zuweisen, um diesen Effekt zu erzielen.
//assume oldAuthor is the old list
Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList();
oldAuthor = newAuthorList;
newAuthorList = null;
Um den Code fließend zu halten (wenn die Codeoptimierung nicht entscheidend ist), müssen Sie einige weitere Vorgänge in der Liste durchführen:
authorsList = authorsList.Where(x => x.FirstName != "Bob").<do_some_further_Linq>;
oder
authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;
LINQ hat seinen Ursprung in der funktionalen Programmierung, die die Unveränderlichkeit von Objekten hervorhebt, und bietet daher keine integrierte Methode zum Aktualisieren der ursprünglichen Liste.
Anmerkung zur Unveränderlichkeit (aus einer anderen Antwort SO):
Hier ist die Definition der Unveränderlichkeit von Wikipedia (Link)
"In der objektorientierten und funktionalen Programmierung ist ein unveränderliches Objekt ein Objekt, dessen Zustand nach seiner Erstellung nicht mehr geändert werden kann."