LINQ Sum of entries based on latest date - c#

I have a table like so:
Code | BuildDate | BuildQuantity
---------------------------------
1 | 2013-04-10 | 4
1 | 2014-09-23 | 1
1 | 2014-08-20 | 2
7 | 2014-02-05 | 4
I want the LINQ query to pick up the LATEST Build date for each Code, pick the BuildQuantity for that Code, and so on for all the records, and sum it up. So for the data above, the result should be 1 + 4 = 5.
This is what I'm trying:
var built = (from tb in db.Builds
orderby tb.BuildDate descending
group tb by tb.Code into tbgrp
select tbgrp.Sum(c => c.BuildQuantity)).First();
This query returns 7... What am I doing wrong?

You are summing all code's BuildQuantities before you take the first, instead you want to sum the firsts.
int built = db.Builds
.GroupBy(b => b.Code)
.Sum(g => g.OrderByDescending(b => b.BuildDate).First().BuildQuantity);

You are looking for the sum of the build quantity of the last entry per code. You're currently ordering before you group, which doesn't actually do anything (after the group, the ordering isn't defined)
So first of, you're looking to get the latest element by code. Lets group first. I'm more comfortable writing through the extension methods:
IGrouping<int, Build> grouped = db.Builds.GroupBy(tb => tb.Code)
we now have the elements grouped. From each group, we want to get the first element ordered descending by build date.
var firsts = grouped.Select(gr => gr.OrderByDescending(gr => gr.BuildDate)
.First())
finally, we can get the sum:
var sum = firsts.Sum(tb => tb.BuildQuantity);
plugging this all together becomes
var sum = db.Builds.GroupBy(tb => tb.Code).
.Select(gr => gr.OrderByDescending(gr => gr.BuildDate).First())
.Sum(tb => tb.BuildQuantity);
Group by has overloads that allows you to roll almost everything in the group.
If you like compact code, you could write
var sum = db.Builds
.GroupBy(tb => tb.Code,
tb => tb,
gr => gr.OrderByDescending(gr => gr.BuildDate)
.First()
.BuildQuantity)
.Sum()
though I wouldn't recommend it from a readability point of view

Related

How to apply self join in Linq Query?

Books Table
Id VendorId ASIN Price
-- -------- ---- ------
1 gold123 123 10
2 sil123 123 11
3 gold456 456 15
4 gold678 678 12
5 sil456 456 12
6 gold980 980 12
I want to write a linq query which will return me rows for which corresponding to every gold if sil vendor id not exist. The last three digit of vendor Id is corresponding ASIN column in that row.
Ex- For gold123 corresponding sil123 exist so that row will not be returned but for gold678 and gold980 corresponding sil not exist. So those rows will be returned.
I tried following
var gold = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("gold"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
var asinsForGold = gold.Select(x => x.ASIN).ToList();
var correspondingSilver = _repository.Query<Books>().Where(x =>
x.VendorId.Contains("sil")
&& asinsForGold.Contains(x.ASIN)).ToList();
var correspondingSilverAsins = correspondingSilver.Select(x => x.ASIN).ToList();
var goldWithoutCorrespondingSilver = gold.Where(x =>
!correspondingSilverAsins.Contains(x.ASIN));
Can We apply self join or better way to get result only in one query instead of two query and several other list statement.
It's just another predicate, "where a corresponding silver vendor doesn't exist":
var goldWoSilver = _repository.Query<Books>()
.Where(x => x.VendorId.Contains("gold"))
.Where(x => !_repository.Query<Books>()
.Any(s => s.ASIN == x.ASIN
&& s.VendorId.Contains("sil"))
.OrderBy(x => x.Id).Skip(0).Take(500).ToList();
In many cases this is a successful recipe: start the query with the entity you want to return and only add predicates. In general, joins shouldn't be used for filtering, only to collect related data, although in that case navigation properties should be used which implicitly translate to SQL joins.
See if it helps -
var goldWithoutCorrespondingSilver = from b1 in books
join b2 in books on b1.ASIN equals b2.ASIN
where b1.VendorId.Contains("gold")
group b2 by b1.VendorId into g
where !g.Any(x => x.VendorId.Contains("sil"))
select g.FirstOrDefault();
What I have done is -
Selected records with matching ASIN
Grouped them by VendorID
Selected ones which do not have sil

Selecting Distinct Count and Sum of columns received as sub-query in Entity Framework

I want to get summarized data for a report that shows total amount & suppliers Count per decision in entity Framework Syntax. My Result needed to include a SUM of Amount and COUNT of total suppliers per decision.
I have a table of suppliers with the following columns:
SupplierNo | Decision | DecisionIssuedOn | Amount | SupplierGroup | SubSupplier
Raw SQL query to get above data for a specific time period is:
SELECT S.Decision, SUM(S.Amount) AS TotalAmount, COUNT(DISTINCT S.SupplierNo) AS SupplierCount
FROM (SELECT * FROM Indentors WHERE Indentors.DecisionIssuedOn BETWEEN '2018-01-01' AND '2018-12-31') S
GROUP BY S.Decision
Which gives data as:
SupplierCount | Amount
-----------------------
Approved 20 | 5000
Rejected 11 | 3000
In-Process 5 | 1500
Now from front end, the condition parameters can be anything from the given pool of options (dropdowns) which when selected add where clause in the exixting query like
WHERE Decision = 'Approved' AND SupplierGroup ='ABC' AND SubSupplier ='zxc'
The problem is I am having a hard time getting the desired result using Entity Framework lambda expressions instead of raw SQL.
What I did so far:
I checked for the availability of Options from fornt-end to build where clause as:
IQueryable<Supplier> suppliers = this.db.suppliers.OrderByDescending(i => i.Id);
if (string.IsNullOrEmpty(selectedSupplierGroup) == false)
{
suppliers = suppliers.Where(i => i.SupplierGroup == selectedSupplierGroup);
}
if (string.IsNullOrEmpty(selectedSubSupplier) == false)
{
suppliers = suppliers.Where(i => i.SubSupplier == selectedSubSupplier);
}
if (string.IsNullOrEmpty(selectedDecision) == false)
{
suppliers = suppliers.Where(i => i.Decision == selectedDecision);
}
if (selectedDecisionIssuedOn.HasValue)
{
suppliers = suppliers.Where(i => i.DecisionIssuedOn >= selectedDecisionIssuedOn);
}
var result = suppliers
.GroupBy(i => i.Decision)
.Select(i => i.SupplierNo).Distinct().Count(); // Gives me error
The error is:
IGrouping does not contain a definition for SupplierNo, and no extension method blah blah blah...
But after that I am unable to get data as the raw query (described above) would get me. Thanks
This should give you a similar result to your SQL query. Give it a try and see how you get on:
var results = suppliers
.Where(i => i.DecisionIssuedOn >= selectedDecisionIssuedOn)
.GroupBy(i => i.Decision)
.Select(group => new
{
Decision = group.Key,
TotalAmount = group.Sum(g => g.Amount),
SupplierCount = group.Select(i => i.SupplierNo).Distinct().Count()
});

Linq select one occurrence

how can I use link to fetch one-to-one relation that does not contain duplicates? Example:
ID | STATUS
1 | CHECKIN
2 | CHECKOUT
2 | CHECKOUT
1 | CHECKIN
3 | CHECKOUT <--
I should only retrieve the ID 3 CHECKOUT because he is not duplicated.
Can you help me using linq?
You need to make a Group and ask for only group items that = 1
Dim nonDuplicates = (From x In query Group By x.Id, x.Status Into grp = Group
Where grp.Count = 1)
The other answer will still retrieve all the duplicated items, just removing duplicates of them. If you want to only retrieve non-duplicated items, as you stated in your original question, this will work for you:
Item singles = items.Where(i => !items.Any(j => !i.Equals(j) && i.id == j.id));

order by and project distinct objects

I have data something like this:
Id | Customer | CartTotal
-------------------------------
1 | a | 100
2 | a | 50
3 | b | 110
4 | b | 128
I need to order it by CartTotal (descending) and return distinct customers
so that I should have this in my result set:
Id | Customer | CartTotal
-------------------------------
4 | b | 128
1 | a | 100
I believe I need to do an order and projection. I'm working with a strongly typed IList<> datasource. I'm new to LINQ.. any help would be greatly appreciated.
Something like the following should do what you're after:
var filteredPurchases = purchases.OrderByDescending(p => p.CartTotal)
.GroupBy(p => p.Customer)
.Select(g => g.First());
It will return the purchase with the maximum CartTotal for each Customer, giving the desired result.
The answers so far, while correct, are significantly less efficient then needed because they 1)sort before grouping and 2)only need the largest element in the first place. Sorting first makes the solution O(n*log(n)).
Taking care of number 1, we can do the following:
var query = purchases
.GroupBy(p => p.Customer)
.Select(g => g.OrderByDescending(p => p.CartTotal).First());
This gets us a solution something like O(n + n * log(n/c), where c is the number of customers. Assuming that orders per customer is roughly constant, it is O(n).
Now, we can do better, by just finding the maximum element for each customer and selecting it in one pass. Unfortunately, the Max operator in Linq makes this more painful than it should be. If you pull down MoreLinq, you can do the following:
var query = purchases
.GroupBy(p => p.Customer)
.Select(g => g.MaxBy(p => p.CartTotal));
This solution is always O(n), regardless of the distribution of purchases to customers. I would also expect it to be the fastest by far on large data sets.
Here's a query expression version:
var query = from cart in carts
orderby cart.CartTotal descending
group cart by cart.Customer into custCarts
select custCarts.First();

LINQ: Selecting items from a list (Group By/Select/Sum & Max!)

Just getting my head around Linq and having lots of fun! Can any one aid me with a query for this:
I have a list of data:
Key Value
Aaa 12
AaA 10
AAa 5
BBB 2
Bbb 1
1. I want to group by Key.ToUpper()
2. For every group I need the Max(Value) & Sum(Value)
3. For every group I want to select the entries
There the Value != Max(value)
the final result should be like this:
Key Max Total
AaA 12 27
AAa 12 27
Bbb 2 3
Thanks!
Update, actually I also need the Key from the Maximum entry:
Key Max Total Correct
AaA 12 27 Aaa
AAa 12 27 Aaa
Bbb 2 3 BBB
:)
var results =
from kvp in source
group kvp by kvp.Key.ToUpper() into g
select new
{
Group = g,
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
} into ag
from x in ag.Group //SelectMany
where x.Value != ag.Max
//for the update to the question - note: possibly ambiguous
let correct = ag.Group.Where(y => y.Value == ag.Max).First().Key
select new
{
Key = x.Key,
Max = ag.Max,
Total = ag.Total,
Correct = correct
};
I kinda like the question because of all the little parts (some are rarely used) that are required to make the answer.
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
Performing multiple aggregations on a group is straightforward, yet challenging if you don't know how.
select a into b
This clause takes everything that happened before and starts a new query with the target. Without it, I'd have to start a new query like this:
var A = ... select a
var B = from b in A
It's important to note that the select into clause removes kvp and g from scope.
from b in source
from a in b.A //SelectMany
This "unpacking" of the child collection turns my query about b's into a query about a's. Unlike the default Enumerable.SelectMany overload, it leaves the parent (b) in scope.
where x.Value != ag.Max
Comparing a child's property with a parent's property? Delightful. It's important to remember to break out where anytime you want to filter, even if you just grouped (there is no HAVING).

Categories

Resources