2009-07-26 17 views
27

Ora vengo in una fase per ottenere tutti i miei dati come un elenco in cache (oggetti) e la prossima cosa che devo fare è rimuovere alcune istanze dall'elenco.Rimuovere le istanze da un elenco utilizzando LINQ o Lambda?

Normalmente, avrei fatto rimuovendo in questo modo:

List<T> list; 
List<T2> toBeRemovedItems; 
// populate two lists 
foreach(T2 item in toBeRemovedItems) 
{ 
    list.Remove(delegate(T one) { 
     // build a condition based on item 
     // return true or false 
    }); 
} 

Per essere più precisi, in realtà ho costruire o compilare l'elenco toBeRemvoedItems di una classe dinamica (non una classe definita formale). Ad esempio, la classe T è qualcosa come MyClass e codici per la rimozione sono:

class MyClass<C> { 
    public string Value1 { get; set; } 
    public int Value2 { get; set; } 
    public C ObjectC { get; set; } 
} 
.... 
List<MyClass<C>> list; 
// populate list 
// populate toBeRemovedItems. Here is an example of hard-coded codes: 
var toBeRemovedLItems = new[] { 
    new { Value1="a", Value2 = 1}, 
    new { Value2="x", Value2 = 10}, 
    ... 
}; 
// toBeRemovedItems may be the result of Select from a collection 
foreach(var item in toBeRemovedLItems) 
{ 
    list.Remove(delegate(MyClass one) { 
     return one.Value1 = item.Value1 && one.Value2 < item.Value2; 
    }); 
} 

Ho provato a cercare Remove() metodo IEnumerable interfaccia da MSDN, ma non riesco a trovare il metodo di Remove() lì (è logico che IEnumerable viene utilizzato solo per l'enumerazione). Nella classe List, esistono diversi metodi sovraccarichi Remove(...). Non sono sicuro se esistono metodi alternativi per rimuovere elementi da un elenco utilizzando le espressioni LINQ o Lambda?

A proposito, ho pensato a un modo per eseguire una query su un elenco per ottenere un sottoinsieme o un nuovo elenco IEnumerable con condizioni Where, simili agli elementi in movimento da un elenco. Tuttavia, preferisco rimuovere elementi dalla mia lista memorizzata, e in alcuni casi non riesco a resettare la proprietà della lista in una classe in un nuovo elenco (set privato ad esempio).

+0

Sembra un ubriacone. Beh, in pratica hai menzionato tutti i possibili metodi nel post stesso ... http://stackoverflow.com/questions/1120336/questo-è-il-modo più facile-diritto-di-profondare-a-listt-removing-unwanted -oggetti –

risposta

40

Si potrebbe utilizzare il metodo RemoveAll:

MyClass one; //initialize MyClass 
list.RemoveAll(item => one.Value1 == item.Value1 && one.Value2 < item.Value2); 
+7

penso che sia in realtà un metodo di lista , non un metodo di estensione – Jimmy

+0

Grazie! Aggiorno il mio post. –

+0

La mia comprensione dei codici è che si rimuove un elemento alla volta. Devo ancora fare un ciclo ciascuno come il suggerimento di Rchard Hein. –

2
foreach(var item in toBeRemovedLItems) { 
    list.RemoveAll(one => one.Value1 == item.Value1 && one.Value2 < item.Value2); 
} 

Troppo tardi di nuovo. Oh bene.

+0

Non sono sicuro se sia possibile passare due parametri a RemoveAll (lambda func) per rimuovere un toBeRemovedItems, senza passare attraverso il ciclo foreach. Di sicuro è già molto leggibile. –

21

È possibile utilizzare il metodo LINQ Dove per filtrare i valori che non devono far parte dell'elenco. Il risultato è un IEnumerable<T> con gli elementi rimossi.

var res = list.Where(item => !(one.Value1 == item.Value1 && one.Value2 < item.Value2)); 

questo non sarà aggiornato il List<T> istanza originale ma invece creerà una nuova IEnumerable<T> con i valori rimossi.

+0

Qual è il perf. colpire facendo questo contro un ciclo for rimuovendo gli articoli? –

4

Sono d'accordo con il suggerimento di Jared di filtrare alcuni elementi, ma sembra che un join su Value1 sarebbe un approccio più efficiente:

var res = from item1 in list 
      join item2 in toBeRemovedList 
      on item1.Value1 equals item2.Value1 
      where item1.Value2 >= item2.Value2 
      select item1; 

Aggiornamento: A quanto pare non riescono a comprensione della lettura - nuovo approccio :

var removeDict = toBeRemovedList.ToDictionary(i => i.Value1, i => i.Value2); 
list.RemoveAll(item => { 
    int itemToRemoveValue2; 
    if(removeDict.TryGetValue(item.Value1, out itemToRemoveValue2)) 
     return item.Value2 < itemToRemoveValue2; 
    return false; 
}); 

Ovviamente, sarebbe ancora meglio se la lista da rimuovere potesse iniziare come un dizionario. In definitiva, stiamo solo cercando di rendere più efficiente la nostra partita su Value1.

+0

Il join è simile a SQL Intersect? o questa query LINQ come quella? –

+0

Ad ogni modo, come ho detto nella mia domanda, se la proprietà della lista non è impostabile, non posso usare questa strategia. È un buon modo solo io ho il controllo della lista. –

+0

Notato - risposta aggiornata. – dahlbyk

5

Se ottengo la domanda correttamente, per produrre un set unico da due List.

Per questo, è possibile utilizzare il seguente lista1

List; Lista lista2;

Elenco elenco3 = elenco1.Tranne (lista 2)

La lista3 conterrà elementi unici.

+0

Questa è la prima risposta che risolve il mio obiettivo, ovvero rimuovere e ricevere l'enumerazione di oggetti che corrispondono al lambda. –

+0

Deve essere lo stesso tipo di raccolta per farlo funzionare. – vapcguy

1

Per gli insiemi che non sono elenchi (non è possibile esporre RemoveAll), è comunque possibile rimuovere gli elementi con un solo liner.

Per sostituire in linea, è sufficiente generare un elenco di elementi da rimuovere, quindi eseguirlo e eseguire la rimozione del codice.

var dictionary = new Dictionary<string, string>(){{"foo", "0"}, {"boo", "1"}, {"goo", "1"}}; 
dictionary 
    .Where(where_item => 
     ((where_item.Key == "foo") && (where_item.Value == "0")) 
     || ((where_item.Key == "boo") && (where_item.Value == "1")) 
    ) 
    .ToList() 
    .ForEach(remove_item => { 
     dictionary.Remove(remove_item.Key); 
    }); 

Per sostituire in copia, è sufficiente generare una enumerazione filtrata e restituire una nuova copia.

var dictionary0 = new Dictionary<string, string>(){{"foo", "0"}, {"boo", "1"}, {"goo", "1"}}; 
var dictionary1 = dictionary0 
    .Where(where_item => 
     ((where_item.Key == "foo") && (where_item.Value == "0")) 
     || ((where_item.Key == "boo") && (where_item.Value == "1")) 
    ) 
    .ToDictionary(each_item => each_item.Key, each_item => each_item.Value); 
0

Forse stai cercando di fare qualcosa del genere?

List<T> firstList; 
List<T2> toBeRemovedItems; 
List<T> finalList; 

foreach(T item in firstList) 
{ 
    toBeRemovedItems = CheckIfWeRemoveThisOne(item.Number, item.Id); 
    if (toBeRemovedItems == null && toBeRemovedItems.Count() == 0) 
     finalList.Add(item); 
} 

Questo è come sono riuscito a risolvere un problema a sbarazzarsi di duplicati tra un List<ViewModel> e List<Model>. Ho usato la funzione CheckIfWeRemoveThisOne per verificare se lo item.Number appartenesse ad un altro elemento, usando l'ID come caratteristica di definizione. Se ha trovato un altro elemento (un duplicato), piuttosto che provare a rimuoverlo dalla lista originale (che stavo tornando a List<Model> e mi è stato dato un List<ViewModel> nella mia funzione in primo luogo, quindi ho avuto i miei dubbi su come io potrei farlo, comunque), ho appena creato una nuova lista - aggiungendo il risultato se è stato trovato ok.

Problemi correlati