Usando o LINQ para remover elementos de uma lista

Digamos que eu tenha uma consulta LINQ como:

var authors = from x in authorsList where x.firstname == "Bob" select x; 

Dado que authorsList é do tipo List , como posso excluir os elementos Author de authorsList que são retornados pela consulta em authors ?

Ou, em outras palavras, como posso excluir todo o primeiro nome de Bob de authorsList ?

Nota: Este é um exemplo simplificado para os propósitos da questão.

Bem, seria mais fácil excluí-los em primeiro lugar:

 authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList(); 

No entanto, isso apenas alteraria o valor de authorsList vez de remover os autores da coleção anterior. Como alternativa, você pode usar RemoveAll :

 authorsList.RemoveAll(x => x.FirstName == "Bob"); 

Se você realmente precisa fazer isso com base em outra coleção, eu usaria um HashSet, RemoveAll e Contains:

 var setToRemove = new HashSet(authors); authorsList.RemoveAll(x => setToRemove.Contains(x)); 

Seria melhor usar List .RemoveAll para realizar isso.

 authorsList.RemoveAll((x) => x.firstname == "Bob"); 

Se você realmente precisa remover itens, e quanto a Exceto ()?
Você pode remover com base em uma nova lista ou remover on-the-fly aninhando o Linq.

 var authorsList = new List() { 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(); 

Você não pode fazer isso com operadores LINQ padrão porque o LINQ fornece consulta, não suporte de atualização.

Mas você pode gerar uma nova lista e replace a antiga.

 var authorsList = GetAuthorList(); authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList(); 

Ou você pode remover todos os itens em authors em uma segunda passagem.

 var authorsList = GetAuthorList(); var authors = authorsList.Where(a => a.FirstName == "Bob").ToList(); foreach (var author in authors) { authorList.Remove(author); } 

Solução simples:

 static void Main() { List myList = new List { "Jason", "Bob", "Frank", "Bob" }; myList.RemoveAll(x => x == "Bob"); foreach (string s in myList) { // } } 

Eu estava vagando, se houver alguma diferença entre RemoveAll e Exceto e os profissionais de usar o HashSet, então eu fiz uma verificação rápida de desempenho 🙂

 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 list = new List(); List toRemove = new List(); 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 l1 = new List( list ); List l2 = new List( list ); List l3 = new List( list ); List l4 = new List( list ); Console.WriteLine( "Be patient, testing..." ); Stopwatch sw1 = Stopwatch.StartNew(); l1.RemoveAll( toRemove.Contains ); sw1.Stop(); Stopwatch sw2 = Stopwatch.StartNew(); l2.RemoveAll( new HashSet( toRemove ).Contains ); sw2.Stop(); Stopwatch sw3 = Stopwatch.StartNew(); l3 = l3.Except( toRemove ).ToList(); sw3.Stop(); Stopwatch sw4 = Stopwatch.StartNew(); l4 = l4.Except( new HashSet( 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(); } } } 

Resultados abaixo:

 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 

Como podemos ver, a melhor opção nesse caso é usar RemoveAll (HashSet)

Essa é uma pergunta muito antiga, mas achei uma maneira muito simples de fazer isso:

 authorsList = authorsList.Except(authors).ToList(); 

Observe que, como a variável de retorno authorsList é uma List , o IEnumerable retornado por Except() deve ser convertido em uma List .

Você pode remover de duas maneiras

 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; 

Eu tive o mesmo problema, se você quiser saída simples com base em sua condição, então a primeira solução é melhor.

Digamos que authorsToRemove seja um IEnumerable que contém os elementos que você deseja remover de authorsList .

Então aqui está outra maneira muito simples de realizar a tarefa de remoção solicitada pelo OP:

 authorsList.RemoveAll(authorsToRemove.Contains); 

Eu acho que você poderia fazer algo assim

  authorsList = (from a in authorsList where !authors.Contains(a) select a).ToList(); 

Embora eu ache que as soluções já fornecidas resolvam o problema de uma maneira mais legível.

Abaixo está o exemplo para remover o elemento da lista.

  List items = new List() { 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 

O LINQ tem suas origens na functional programming, que enfatiza a imutabilidade dos objects, por isso não fornece uma maneira integrada de atualizar a lista original no local.

Nota sobre imutabilidade (extraída de outra resposta SO):

Aqui está a definição de imutabilidade da Wikipedia (link)

“Na functional programming e orientada a object, um object imutável é um object cujo estado não pode ser modificado depois de criado.”

Eu acho que você só tem que atribuir os itens da lista de autor para uma nova lista para ter esse efeito.

 //assume oldAuthor is the old list Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList(); oldAuthor = newAuthorList; newAuthorList = null; 

Para manter o código fluente (se a otimização de código não for crucial) e você precisaria fazer algumas operações adicionais na lista:

 authorsList = authorsList.Where(x => x.FirstName != "Bob").; 

ou

 authorsList = authorsList.Where(x => !setToRemove.Contains(x)).; 

É muito simples:

 authorsList.RemoveAll((x) => x.firstname == "Bob");