web-dev-qa-db-fra.com

Comment entrer dans LINQ une rangée pour chaque clé

J'ai une liste avec:

 EMP_ID | UPDATED_DATE | MARK 
------ | ------------ | ----
 111 | 01/01/2015 | 99 
 111 | 01/01/2013 | 85 
 111 | 01/01/2017 | 80 
 222 | 01/01/2011 | 70 
 222 | 01/01/2015 | 55 
 222 | 01/01/2002 | 60 

Je dois sélectionner une ligne pour chaque ID, avec le dernier UPDATED_DATE, Dans notre etc:

 EMP_ID | UPDATED_DATE | MARK 
------ | ------------ | ----
 111 | 01/01/2017 | 80 
 222 | 01/01/2015 | 55 

C'est le code de commande:

empMarksList.OrderBy(x=>x.EMP_ID).ThenBy(y=>y.UPDATED_DATE)
9
user1012506

Utilisez GroupBy:

var items = empMarksList
                   .GroupBy(e => e.EMP_ID)
                   .Select(grp => grp.OrderByDescending(v => v.UPDATED_DATE).First());

Ou si vous voulez un dictionnaire:

var dict = empMarksList
              .GroupBy(e => e.EMP_ID)
              .ToDictionary(grp => grp.Key,
                            grp => grp.OrderByDescending(v => v.UPDATED_DATE).First());
11
Amir Popovich

Une autre option est:

var items = context.EmpMarks
    .GroupBy(e => e.EMP_ID, (k, g) => g
        .FirstOrDefault(e => g.Max(v => v.UPDATED_DATE) == e.UPDATED_DATE));

Ce qui devrait générer GROUP BY en SQL.

3
arekzyla

Je préfère cette variante, mais c'est la même chose que la réponse d'Amir:

var query =
    empMarksList
        .GroupBy(x => x.EMP_ID)
        .SelectMany(x => x.OrderByDescending(y => y.UPDATED_DATE).Take(1));
3
Enigmativity

Vous pouvez utiliser quelque chose comme ceci: 

var result = empMarksList.GroupBy(x => x.Id)
    .Select(g => 
        g.Aggregate((a, x) => a == null || a.UPDATRED_DATE < x.UPDATRED_DATE ? x : a));

C'est un peu plus lourd que d'utiliser OrderBy, mais de cette façon, vous ne pourrez pas commander toutes les sous-collections, ce qui est un peu excessif ici et utilise plus de ressources.

EDIT: Après une réponse de @arekzyla, je me suis rendu compte que mon option pourrait aussi être écrite comme suit:

var items = empMarksList.GroupBy(
   x => x.Id,
   (k, g) => g.Aggregate((a, x) => a == null || a.UPDATRED_DATE < x.UPDATRED_DATE ? x : a));

Il est moins lisible, mais il aura une procédure pas à pas pour deux collections, ce qui est négligeable dans la plupart des cas.

Je ne sais pas dans quel cas le code SQL généré serait plus optimal, donc il pourrait être intéressant de le vérifier.

1
Andrey Tretyak