LINQ: How to get the Max Id with a group by clause? - c#

I am looking for a way in LINQ to get a max Id record by using 'Group By' clause
Consider the following Sample data
Table: ProcessAud
ProcessSeq ProjectSeq ProjectValue Active
11 1 50000 Y
12 1 10000 Y
13 2 70000 Y
14 2 90000 Y
In which I want to get two records as a list such that is second and fourth
records (i.e) ProcessSeq 12 and 14. And I tried it like following
var ProcessAudList = ProcessAudService.FilterBy(x => x.Active == "Y"
).GroupBy(x => x.ProjectSeq).Max().ToList();
It is not working properly, So how to do it in LINQ. Please anybody help.

You want to pick top record from each group.
var ProcessAudList = ProcessAudService.Where(x => x.Active == "Y")
.GroupBy(x => x.ProjectSeq, (key,g)=>g.OrderByDescending(e=>e.ProjectValue).First());
Check demo code

When you use GroupBy extension, method will return you IGrouping instance and you should query IGrouping instance like below;
var ProcessAudList = collection.Where(x => x.Active == "Y").GroupBy(x => x.ProjectSeq).Select(x => x.OrderByDescending(a => a.ProcessSeq).FirstOrDefault()).ToList();
Hope this helps

You're most of the way there, but Max is the wrong term to use.
Each IGrouping is an IEnumerable (or IQueryable) sequence of its own, so you can use OrderBy and First clauses to get the answer you need:
var ProcessAudList = ProcessAudService
.FilterBy(x => x.Active == "Y")
.GroupBy(x => x.ProjectSeq)
.Select(grp => grp.OrderByDescending(x => x.ProcessSeq).First())
.ToList();
The Select clause will process each of the groups, order the groups descending by ProcessSeq and select the first one. For the data you provided this will select the rows with ProcessSeq equal to 12 and 14.

With this code you can get all max id in foreach
var res = from pa in ProcessAud
group Cm by pa.ProjectSeq into Cm1
select new
{
_max = Cm1.Max(x => x.ProcessSeq)
};
foreach (var item in res)
{
//item._max have biggest id in group
}

Related

c# - How to remove matching items from a list using Linq or otherwise

I'm trying to remove matching items from a list, which seems like a pretty simple task, but as luck would have it, I can't figure it out.
Example list:
List<int> points1 = new List<int>
{
1, 2, 3, 3
};
I'm trying to get uniquePoints1 to be 1,2
I know there is .Distinct() but that would return 1,2,3 which is not what I want.
I've also tried the following along with .Distinct() but I get a red line saying Comparison made to the same variable, did you mean to compare to something else?
List<int> uniquePoints1 = points1.Where(x => x == x);
List<int> uniquePoints1 = points1.RemoveAll(x => x == x);
Any help or direction is appreciated.
You can use the GroupBy method to group the items, and then return only the numbers from groups that have a count of 1:
List<int> uniquePoints = points
.GroupBy(x => x) // Group all the numbers
.Where(g => g.Count() == 1) // Filter on only groups that have one item
.Select(g => g.Key) // Return the group's key (which is the number)
.ToList();
// uniquePoints = { 1, 2 }
"Group by" to the rescue!
This is a LINQ variant -- see other answers for a non-LINQ version
var nonDuplicatedPoints = from p in points1
group p by p into g
where g.Count() == 1
select g.Key;

Determine Duplicate data using LINQ to EF

I have a dataset that i want to groupby to determine duplicate data.
Example i have a dataset that looks like this.
|id | Number | ContactID
1 1234 5
2 9873 6
3 1234 7
4 9873 6
Now i want to select data that has more than one occurrence of Number but only if the ContactID is not the same.
So basically return
| Number | Count |
1234 2
Any help would be appreciated using LINQ to EF, thanks.
Update:
All thanks to #DrCopyPaste, as he told me that I misunderstood your problem. Here is the correct solution:-
var result = from c in db.list
group c by c.Number into g
let count = g.GroupBy(x => x.ContactID).Where(x => x.Count() == 1).Count()
where count != 0
select new
{
Number = g.Key,
Count = count
};
Sample Fiddle.
This query avoids making a custom IEqualityComparer as if I remember correctly don't think they play well with EF.
var results = data.GroupBy(number => number.Number)
.Where(number => number.Count() > 1)
.Select(number => new
{
Number = number.Key,
Count = number.GroupBy(contactId => contactId.ContactId).Count(x => x.Count() == 1)
})
.Where(x => x.Count > 0).ToList();
Fiddle
It does an initial GroupBy to get all Numbers that are duplicated. It then selects a new type that contains the number and a second GroupBy that groups by ContactId then counts all groups with exactly one entry. Then it takes all results whose count is greater than zero.
Have not testing it against EF, but the query uses only standard Linq operators so EF shouldn't have any issues translating it.
Another way of doing this(using 1 level of grouping):
var results = data
.Where(x => data.Any(y => y.Id != x.Id && y.Number == x.Number && y.ContactId != x.ContactId))
.GroupBy(x => x.Number)
.Select(grp => new { Number = grp.Key, Count = grp.Count() })
.ToList();
Fiddle

Filter some unique Data with LINQ and C#

i am very new with C# and MVC.
My Problem:
I have a list OF IDs
int[] mylist = {10, 23}
I try to query some data from DB
var result = db.tableName.Where(o => mylist.Any(y => y == o.item_ID && o.readed)).ToList();
This is what I get with the query:
item_ID Product_ID readed
277 1232 1
277 1233 1
277 1235 1
280 1235 1
What I need is:
item_ID Product_ID readed
277 1235 1
280 1235 1
If I change "any" to "all" i don't get any results, but I have definitely one item where the condition fits.
I think its more like make a query with id 277, then a query with 280 and then merge the list and return only where where "Product_ID" match.
Any ideas?
I assume that what you need is this:
var temp = db.tableName.Where(o => mylist.Any(y => y == o.item_ID && o.readed))
.ToList();
// Find the Product_id which appeared more than one time
// By the way, this assumes that there is at least one product_Id whihc has appeared more than one time
var neededProductID = temp.GroupBy(x => x.Product_ID)
.Where(x => x.Count() > 1)
.First()
.Key;
// Filter the result by neededProductID
var result = temp.Where(x => x.Product_ID == neededProductID).ToList();
Also, if there could be more tha one Product_ID which has apperaed more than one time, then you can consider this:
var neededProductID = temp.GroupBy(x => x.Product_ID)
.Where(x => x.Count() > 1)
.Select(x => x.Key)
.ToList();
var result = temp.Where(x => neededProductID.Any(y => y == x.Product_ID)).ToList();
By the way, you don't need All(). It tells you if all the elements in a collection match a certain condition.
You can use the following
var result = db.tableName.Where(o => mylist.conains(o.item_ID)
&& o.readed).ToList();

Linq Group selection

I need to filter these results to only show the first and last row from each group. I've tried a couple of approaches with no luck. Below is the Linq query and the results.
Linq Query:
var currentAndHistoricalOnly = ViewCustomerGLAndPurchaseRecord
.Where(g=>g.CashierDate > new Datetime(2012,10,01)
&& g.CashierDate < new DateTime(2012,12,01))
.GroupBy(x => x.TransactionId)
.OrderByDescending(d => d.First().CashierDate);
Remember, I'm trying to keep the same grouping but filter(remove records) each group and take only the first record and the last record of each group.
So instead of:
Group 1:
Record 1
Record 2
Record 3
Record 4
Record 5
Group 2:
Record 1
Record 2
Record 3
Record 4
I need:
Group 1:
Record 1
Record 5
Group 2:
Record 1
Record 4
Maybe something like this:
var currentAndHistoricalOnly = ViewCustomerGLAndPurchaseRecord
.Where(g=>g.CashierDate > new DateTime(2012,10,01) && g.CashierDate < new DateTime(2012,12,01))
.GroupBy(x => x.TransactionId)
.Select(g=> new {
Last = g.OrderByDescending(c=>c.CashierDate).FirstOrDefault(),
First = g.OrderBy(c=>c.CashierDate).FirstOrDefault(),
})
.ToList();
You can use the last and first methods
Try this instead:
var list = ViewCustomerGLAndPurchaseRecord
.Where(g=>g.CashierDate > new DateTime(2012,10,01)
&& g.CashierDate < new DateTime(2012,12,01))
.GroupBy(x => x.TransactionId)
.OrderByDescending(d => d.First().CashierDate)
.Select(d =>
{
var result = new List<ViewCustomerGLAndPurchaseRecord>();
result.Add(d.FirstOrDefault());
result.Add(d.LastOrDefault());
return result;
});
If you are using Entity Framework, Use AsEnumerable() before select.
Hope this will help !!

Selecting distinct with certain condition

I have values in a list:
List1
ID groupID testNo
1 123 0
2 653 1
3 776 6
4 653 0
I want to write a T-Sql or linq or lambda expression, so that whenever there is a duplicate it should pick the one with value !=0
I am using this expression but it is basically not giving the results I want.
var list2 = list1.GroupBy(x => x.testNo).Select(y => y.First());
How can I get the results so groupID 653 is chosen with testNo 1 with rest of the records?
There are a few approaches you could take. I don't know if any of them are full proof. One would be to do an OrderBy on testNo so that items with a non zero testNo will come up before those with 0.
var list2 = list1.Orderby(y => y.testNo).GroupBy(x => x.testNo).Select(z => z.FirstOrDefault());
If you can guarantee that testNo = 0 only occurs for dupes then the easiest way is just to use a where.
var list2 = list1.Where(x => x.testNo > 0).First();
This should give you the desired results:
var list2 = list1.GroupBy(x => x.groupID)
.Select(x => list1.Single(item => item.groupID == x.Key
&& item.testNo == x.Max(y => y.testNo)))
.ToList();
Basically, group by groupID and then select each item from the original list1 that matches the distinct groupID and has the max value for testNo for that groupID.
var result = list.GroupBy(x => x.groupID).Select(g => g.Count() == 1 ? g.First() : g.First(x => x.testNo != 0));

Categories