Consider a function (GetData) which returns the following result set where the first column is division id and the second one is "TotalSales":
DivisionID: 3 500
DivisionID: 3 500
DivisionID: 3 500
DivisionID: 4 800
DivisionID: 4 800
DivisionID: 5 50
I need to write a LINQ query to get the following result:
DiviosnID 3: (500 * 3) - 500 = 1000
DiviosnID 4: (800 * 2) - 800 = 800
DiviosnID 5: 0 /*this since it's happening only once*/
So the total becomes: 1000 + 800 = 1800
Finally, this value should be multiplied by -1, which results in -1800.
The following LINQ query gets the job done, however, it's IMSHO scary. The question is that whether it can be re-written to perform faster and look nicer?! Please note there's a third column there, just like the TotalSales named "TotalPurchases" which I need to do the same calculation for.
GetData()
.Where(t => t.DivisionId != 0)
.GroupBy(t => t.DivisionId)
.Where(g => g.Count() > 1)
.Select(g => new MyEntity
{
TotalSales = g.Sum(n => n.TotalSales) - (g.Sum(n => n.TotalSales) / g.Count()),
TotalPurchases = g.Sum(n => n.TotalPurchases) - (g.Sum(n => n.TotalPurchases) / g.Count())
})
.Union(Enumerable.Repeat(new MyEntity(), 1))
.Aggregate((t1, t2) => new MyEntity
{
TotalSales = -(t1.TotalSales + t2.TotalSales),
TotalPurchases = -(t1.TotalPurchases + t2.TotalPurchases),
});
Thank you
A quick first attempt:
var consolidatedData = GetData()
.GroupBy(t => t.DivisionId)
.Where(g => g.Skip(1).Any(i => i.DivisionId != 0))
.Select(g => new
{
TotalSales = -(g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales)),
TotalPurchases = -(g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases))
});
var overallSales = consolidatedData.Sum(i => i.TotalSales);
var overallPurchases = consolidatedData.Sum(i => i.TotalPurchases);
Using Skip, you avoid the potentially expensive Count() being run on every group - it just Skips one item, and sees if there's anything left.
By building the result into an IEnumerable of anonyomous objects, the code is simplified - you can then query it afterewards when you want the final sums. Note that Average is also used in place of Sum / Count.
The Aggregate is removed - you only compute the final sum at the end when you need it.
My proposition:
var result = data
.Where(t => t.DivisionID != 0)
.GroupBy(t => t.DivisionID)
.Select(g => new MyEntity
{
TotalSales = g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales),
TotalPurchases = g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases)
})
.Aggregate(new MyEntity(), (t1, t2) => new MyEntity
{
TotalSales = t1.TotalSales - t2.TotalSales,
TotalPurchases = t1.TotalPurchases - t2.TotalPurchases,
});
Checking for Where(g => g.Count() > 1)
is not necessary, because SUM and AVG for 1 element group is equal, so select will return 0. I also removed Union(Enumerable.Repeat(new MyEntity(), 1))
and I added seed
to aggregate call - this is start value.
Another one:
var result = data
.Where(t => t.DivisionID != 0)
.GroupBy(t => t.DivisionID)
.Select(g => new MyEntity
{
TotalSales = g.Sum(n => n.TotalSales) - g.Average(n => n.TotalSales),
TotalPurchases = g.Sum(n => n.TotalPurchases) - g.Average(n => n.TotalPurchases)
})
.GroupBy(t => 0) // create single group
.Select(g => new MyEntity
{
TotalSales = -g.Sum(t => t.TotalSales),
TotalPurchases = -g.Sum(t => t.TotalPurchases)
})
.SingleOrDefault();
Assuming that TotalSales
is constant for DivisionId
you can use that:
var result = data
.Where(t => t.DivisionID != 0)
.GroupBy(t => t.DivisionID)
.Select(g => new MyEntity
{
TotalSales = g.Skip(1).Sum(n => n.TotalSales),
TotalPurchases = g.Skip(1).Sum(n => n.TotalPurchases)
})
.Aggregate(new MyEntity(), (t1, t2) => new MyEntity
{
TotalSales = t1.TotalSales - t2.TotalSales,
TotalPurchases = t1.TotalPurchases - t2.TotalPurchases,
});
var result = data
.Where(t => t.DivisionID != 0)
.GroupBy(t => t.DivisionID)
.Select(g => new MyEntity
{
TotalSales = g.Skip(1).Sum(n => n.TotalSales),
TotalPurchases = g.Skip(1).Sum(n => n.TotalPurchases)
})
.GroupBy(t => 0) // create single group
.Select(g => new MyEntity
{
TotalSales = -g.Sum(t => t.TotalSales),
TotalPurchases = -g.Sum(t => t.TotalPurchases)
})
.SingleOrDefault();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.