web-dev-qa-db-fra.com

Supprimer des instances d'une liste à l'aide de LINQ ou Lambda?

J'arrive maintenant sur le point d'obtenir toutes mes données sous forme de liste dans le cache (objets) et ma prochaine tâche consiste à supprimer certaines occurrences de la liste.

Normalement, je supprimerais comme ceci:

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
    });
}

Pour être plus spécifique, je construis ou remplis la liste toBeRemvoedItems d'une classe dynamique (pas une classe définie formelle). Par exemple, la classe T ressemble à MyClass et les codes de suppression sont les suivants:

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;
    });
}

J'ai essayé de rechercher la méthode Remove() dans l'interface IEnumerable à partir de MSDN, mais je ne parviens pas à trouver la méthode Remove() (il est logique que IEnumerable soit utilisé uniquement pour l'énumération). Dans la classe List, il existe plusieurs méthodes Remove(...) surchargées. Je ne sais pas s'il existe d'autres moyens de supprimer des éléments d'une liste à l'aide d'expressions LINQ ou Lambda?

En passant, j’ai pensé à un moyen de faire une requête sur une liste pour obtenir un sous-ensemble ou une nouvelle liste IEnumerable avec des conditions Where, comme le déplacement d’éléments d’une liste. Cependant, je préfère supprimer les éléments de ma liste en cache, et dans certains cas, je ne peux tout simplement pas réinitialiser la propriété list d'une classe à une nouvelle liste (ensemble privé par exemple).

27
David.Chu.ca

Vous pouvez utiliser la méthode RemoveAll :

MyClass one; //initialize MyClass
list.RemoveAll(item => one.Value1 == item.Value1 && one.Value2 < item.Value2);
40
Francis B.

Vous pouvez utiliser la méthode Where de LINQ pour filtrer les valeurs qui ne devraient pas faire partie de la liste. Le résultat est un IEnumerable<T> avec les éléments supprimés. 

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

Cela ne mettra pas à jour l'instance List<T> d'origine, mais créera un nouveau IEnumerable<T> avec les valeurs supprimées. 

21
JaredPar

Si je comprends bien la question, produire un ensemble unique à partir de deux listes.

Pour cela, vous pouvez utiliser les éléments suivants

Liste liste1; Liste liste2;

List list3 = list1.Except (list2)

La liste3 contiendra des éléments uniques.

5
Bhaskar

Je suis d'accord avec la suggestion de Jared de filtrer certains éléments, mais il semble qu'une approche join sur Value1 serait une approche plus efficace:

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

Mise à jour: Apparemment, je ne parviens pas à comprendre la lecture - nouvelle approche:

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;
});

Bien entendu, il serait même préférable que votre liste à supprimer puisse commencer par un dictionnaire. En fin de compte, nous essayons simplement de rendre notre correspondance sur Value1 plus efficace.

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

Encore trop tard. Tant pis.

2
Richard Hein

Pour les collections qui ne sont pas des listes (vous ne pouvez pas exposer RemoveAll), vous pouvez toujours supprimer des éléments avec une ligne.

Pour remplacer inline, générez simplement une liste d'éléments à supprimer, puis parcourez-la et exécutez remove code.

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);
    });

Pour remplacer en copie, il suffit de générer un énumérable filtré et de renvoyer une nouvelle copie.

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);
1
Alex

Peut-être que vous essayez de faire quelque chose comme ça?

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);
}

C’est ainsi que j’ai réussi à résoudre un problème de suppression des doublons entre un List<ViewModel> et un List<Model>. J'ai utilisé la fonction CheckIfWeRemoveThisOne pour vérifier si le item.Number appartenait à un autre élément, en utilisant l'ID comme caractéristique de définition. Si elle trouve un autre élément (un duplicata), plutôt que d'essayer de le supprimer de la liste d'origine (pour lequel je récupérais un List<Model> et que l'on me donnait un List<ViewModel> dans ma fonction, j'ai donc douté de la de toute façon), je viens de construire une nouvelle liste - en y ajoutant le résultat si elle s’avérait satisfaisante.

0
vapcguy