Linq (somme sans doublons)

Signaler
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
-
cs_tulesais
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
-
Bonjour tout le monde !!!

je sais je sais ... c'est un problème de débutant ... faut bien commencer un jour non ??? :-)


j'ai la table suivante :

libelle montant
famille1 500€
famille2 1000€
famille3 800€
famille1 500€

je souhaiterais obtenir le montant total sans récupérer les doublons : soit 2300€ et non pas 2800€ (il ne faut prendre qu'un seul enregistrement pour la famille1)

je suppose qu'il faut utiliser un SUM avec count=1 mais je n'y arrive pas...

pourriez vous m'aider ???


merci d'avance pour votre aide !!!!!!!!!!!!

13 réponses

Messages postés
14113
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
22 janvier 2020
331
Bonsoir et oui, il faut bien débuter.

Il y a cette page, pleine de petits exemples.

Entre autre Distinct...


Whismeril
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
31
Salut,

La solution la plus simple et la plus performante est évidemment de faire un Distinct() sur ta liste et ensuite de faire la somme des éléments restants avec Sum().

Si tu ne connaissait pas la méthode Distinct, tu pouvais toujours créer une nouvelle liste dans laquelle tu mettais les éléments de la première liste s'ils n'y étaient pas déjà, puis faire la somme des éléments de cette deuxième liste. En gros, faire toi-même la méthode Distinct.

Sinon, tu peux aussi te débrouiller (mais de manière complexe, pas logique et lente) avec Sum et Count.
Dans ta méthode Sum, il faut définir un sélecteur. Au lieu de simplement faire elt => elt, si divises la valeur par son nombre d'occurrences dans la liste, tu auras le résultat que tu cherches. Dans ton exemple, on ajoute 500/2, 1000/1, 800/1 et 500/2.
De plus, il faut faire attention avec cette méthode à ne pas perdre la virgule en route lors des divisions, donc de faire un cast en double ou en float.

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
14113
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
22 janvier 2020
331
Pourquoi faire simple quand on peut faire compliqué!


Whismeril
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
merci beaucoup pour votre aide !!!

j'ai réussi à trouver la requête qui va bien par contre je n'arrive pas à l'exprimer en linq :-(

select sum(MONTANT) from Table A
where ID (select min(ID) from Table B where A.LIBELLE B.LIBELLE)

Est ce que cela est réalisable ???


Merci encore pour toute votre aide !!!
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
31
Là, non seulement c'est se compliquer la vie, mais en plus, ce n'est pas réalisable si tu n'as pas d'ID, et enfin, ce n'est pas performant.

Comme on te l'a dit auparavant : d'abord tu fais un Distinct() et ensuite tu fais un Sum().

Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
alors ...

pour le distinct (pas de message d'erreur) :

var w_Distinct = (
from w_Item in _ventilMaintMatColl
select w_Item.IdActivite
).Distinct().ToList();

pour le montant (la ca gueule sur le where) :

var w_Montant = (
from w_Item in _ventilMaintMatColl
where w_Item.Id == w_Distinct
select w_Item.MontantActivite
).Sum();

j'y suis presque ??? :-)
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
j'ai pondu ca :


var w_Distinct = (
from w_Item in _ventilMaintMatColl
select new {montant = w_Item.MontantActivite}
).Distinct().ToList();
var w_Montant = (
from w_Item in w_Distinct
select w_Item.montant
).Sum();


je test et je vous tiens au courant !!!
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
ca a l'air de fonctionner :-)

super !!!

ce fut dur mais j'y suis arrivé ... avec votre aide !!!


merci !!!
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
31
Méthodes d'extension de LINQ :
int resultat _ventilMaintMatColl.Select(elt> elt.MontantActivite).Distinct().Sum();


Syntaxe de LINQ :
int resultat = (from elt in _ventilMaintMatColl select elt.MontantActivite).Distinct().Sum();


Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
oups... chuis allé trop vite !!!

je présume que le premier distinct n'est pas ce que j'attend !!!
moi je veux faire un distinct sur IdActivite et non sur le montant

var w_Distinct = (
from w_Item in _ventilMaintMatColl
select new {montant = w_Item.MontantActivite}
).Distinct().ToList();
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
c'est mieux ca krimog ?
(merci pour ta patience) :-)


var w_Distinct =
(
from w_Item in VentilMaintMatColl
select new { activite w_Item.IdActivite, montant w_Item.MontantActivite }
)
.Distinct().ToList();
Messages postés
1860
Date d'inscription
lundi 28 novembre 2005
Statut
Modérateur
Dernière intervention
14 février 2015
31
Au final, non, c'est pas mieux.
Si tu fais un new dans le select, le distinct ne marchera pas (puisqu'il va faire des objets avec des références différentes, donc le distinct va forcément toutes les garder).

Tu veux faire un Distinct sur le Montant, le Libellé, l'un ET l'autre ou l'un OU l'autre ?
Premier cas, CF pour précédent post.

Pour les autres, c'est plus complexe.

Je viens de faire une méthode d'extension (que je n'ai pas testé - vérifie donc que le résultat est bien celui attendu).
Il faut placer ça en dehors de ta classe :
public static class ExtensionMethods
{
        public static IEnumerable<T> Distinct<T>(this IEnumerable<T> collection, Func<T, T, bool> comparer)
        {
            List<T> list = new List<T>();
            foreach (T elt1 in collection)
            {
                if (!list.Any(elt2 => comparer(elt1, elt2))) list.Add(elt1);
            }
            return list;
        }
}

Cette méthode est une surcharge de la méthode Distinct, qui te permet de prendre tous les éléments d'une liste distincts, en utilisant ton délégué (comparer) comme comparateur.
Il crée une nouvelle liste vide. Il parcours ta liste (l'ancienne). Pour chaque élément, il regarde si la nouvelle liste a déjà un élément égal (selon le comparateur qu'on lui donne). Si non, il ajoute l'élément à la nouvelle liste.

Une fois que c'est fait :
// Distinct sur Libellé :
int montantTotal VentilMaintMatColl.Distinct((o1, O2)> (o1.IdActivite == o2.IdActivite)).Sum(o => o.MontantActivite);

// Distinct sur Montant et Libelle :
int montantTotal VentilMaintMatColl.Distinct((o1, o2)> (o1.IdActivite == o2.IdActivite && o1.MontantActivite == o2.MontantActivite).Sum(o => o.MontantActivite);

// Distinct sur Montant ou Libelle : 
int montantTotal VentilMaintMatColl.Distinct((o1, o2)> (o1.IdActivite == o2.IdActivite || o1.MontantActivite == o2.MontantActivite).Sum(o => o.MontantActivite);


Krimog : while (!(succeed = try())) ;
- Nous ne sommes pas des décodeurs ambulants. Le style SMS est prohibé. -
Messages postés
177
Date d'inscription
mercredi 2 juin 2004
Statut
Membre
Dernière intervention
11 avril 2013
1
wouah wouah wouah ... merci beaucoup :-)

je t'en dirais plus en début de semaine prochaine ! je n'aurais pas le temps de m'en occuper avant :-(

Merci encore pour toute ton aide et bon weekend ... en avance :-)