Dites que j'ai une requête LINQ telle que:
var authors = from x in authorsList
where x.firstname == "Bob"
select x;
Étant donné que authorsList
est de type List<Author>
, comment puis-je supprimer les éléments Author
de authorsList
qui sont renvoyés par la requête dans authors
?
Ou, autrement dit, comment puis-je supprimer tous les prénoms équivalents à Bob de authorsList
?
Remarque: Ceci est un exemple simplifié aux fins de la question.
Eh bien, il serait plus facile de les exclure en premier lieu:
authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList();
Cependant, cela ne ferait que modifier la valeur de authorsList
au lieu de supprimer les auteurs de la collection précédente. Vous pouvez aussi utiliser RemoveAll
:
authorsList.RemoveAll(x => x.FirstName == "Bob");
Si vous avez vraiment besoin de le faire sur la base d'une autre collection, j'utiliserais un hachage, une suppression et un contenu:
var setToRemove = new HashSet<Author>(authors);
authorsList.RemoveAll(x => setToRemove.Contains(x));
Il serait préférable d'utiliser List <T> .RemoveAll pour accomplir cela.
authorsList.RemoveAll((x) => x.firstname == "Bob");
Si vous avez vraiment besoin de supprimer des éléments, qu’en est-il de Except ()?
Vous pouvez supprimer en fonction d'une nouvelle liste, ou supprimer à la volée en imbriquant le Linq.
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();
Vous ne pouvez pas faire cela avec des opérateurs LINQ standard, car LINQ fournit une prise en charge des requêtes et non des mises à jour.
Mais vous pouvez générer une nouvelle liste et remplacer l'ancienne.
var authorsList = GetAuthorList();
authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList();
Ou vous pouvez supprimer tous les éléments de authors
lors d'un deuxième passage.
var authorsList = GetAuthorList();
var authors = authorsList.Where(a => a.FirstName == "Bob").ToList();
foreach (var author in authors)
{
authorList.Remove(author);
}
Solution simple:
static void Main()
{
List<string> myList = new List<string> { "Jason", "Bob", "Frank", "Bob" };
myList.RemoveAll(x => x == "Bob");
foreach (string s in myList)
{
//
}
}
Je me promenais, s'il y a une différence entre RemoveAll et Except et les pros de l'utilisation de HashSet, alors j'ai fait une vérification rapide des performances :)
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();
}
}
}
Résultats ci-dessous:
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
Comme on peut le constater, la meilleure option dans ce cas consiste à utiliserRemoveAll (HashSet)
C'est une très vieille question, mais j'ai trouvé un moyen très simple de le faire:
authorsList = authorsList.Except(authors).ToList();
Notez que, puisque la variable de retour authorsList
est un List<T>
, le IEnumerable<T>
renvoyé par Except()
doit être converti en un List<T>
.
Vous pouvez enlever de deux manières
var output = from x in authorsList
where x.firstname != "Bob"
select x;
ou
var authors = from x in authorsList
where x.firstname == "Bob"
select x;
var output = from x in authorsList
where !authors.Contains(x)
select x;
J'ai eu le même problème, si vous voulez une sortie simple basée sur votre condition where, la première solution est préférable.
Dites que authorsToRemove
est un IEnumerable<T>
qui contient les éléments que vous souhaitez supprimer de authorsList
.
Ensuite, voici un autre moyen très simple d'accomplir la tâche de suppression demandée par l'OP:
authorsList.RemoveAll(authorsToRemove.Contains);
Je pense que tu pourrais faire quelque chose comme ça
authorsList = (from a in authorsList
where !authors.Contains(a)
select a).ToList();
Bien que je pense que les solutions déjà données résolvent le problème de manière plus lisible.
Ci-dessous, l'exemple pour supprimer l'élément de la liste.
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
je pense qu'il vous suffit d'assigner les éléments de la liste des auteurs à une nouvelle liste pour que cela prenne effet.
//assume oldAuthor is the old list
Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList();
oldAuthor = newAuthorList;
newAuthorList = null;
Pour que le code soit fluide (si l'optimisation du code n'est pas cruciale) et que vous deviez effectuer d'autres opérations dans la liste:
authorsList = authorsList.Where(x => x.FirstName != "Bob").<do_some_further_Linq>;
ou
authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;
LINQ tire ses origines de la programmation fonctionnelle, qui met l'accent sur l'immuabilité des objets, de sorte qu'elle ne fournit pas de moyen intégré de mettre à jour la liste d'origine sur place.
Note sur l'immutabilité (extraite d'une autre réponse SO):
Voici la définition de l'immuabilité de Wikipedia (lien)
"Dans la programmation fonctionnelle et orientée objet, un objet immuable est un objet dont l'état ne peut pas être modifié après sa création."