Selecting fields in grouping - c#

I have data like the following inside my DataTable:
id vrn seenDate
--- ---- --------
1 ABC 2017-01-01 20:00:05
2 ABC 2017-01-01 18:00:09
3 CCC 2016-05-05 00:00:00
I am trying to modify the data to only show vrn values with the most recent date. This is what I have done so far:
myDataTable.AsEnumerable().GroupBy(x => x.Field<string>("vrn")).Select(x => new { vrn = x.Key, seenDate = x.Max(y => y.Field<DateTime>("seenDate")) });
I need to modify the above to also select the id field (i.e. I do not want to group on this field, but I want to have it included in the resulting data set).
I cannot put in x.Field<int>("id") in the Select() part, as the Field clause does not exist.

You need an equivalent of MaxBy method from MoreLINQ.
In standard LINQ it can be emulated with OrderByDescending + First calls:
var result = myDataTable.AsEnumerable()
.GroupBy(x => x.Field<string>("vrn"))
.Select(g => g.OrderByDescending(x => x.Field<DateTime>("seenDate")).First())
.Select(x => new
{
vrn = x.Field<string>("vrn"),
id = x.Field<int>("id"),
seenDate = x.Field<DateTime>("seenDate"),
});

You can use select new like this to select anything you want from your data
var query = from pro in db.Projects
select new { pro.ProjectName, pro.ProjectId };

If you may have few ids in the same vrn with same max date then the following would work:
IEnumerable<DataRow> rows = myDataTable.AsEnumerable()
.GroupBy(x => x.Field<string>("vrn"))
.Select(x => new
{
Grouping = x,
MaxSeenDate = x.Max(y => y.Field<DateTime>("seenDate"))
})
.SelectMany(arg =>
arg.Grouping.Where(y => y.Field<DateTime>("seenDate") == arg.MaxSeenDate));
It will retrun an IEnumerable of the original DataRow so you have all your fields there.
Or you can add another select to have only the fields you need.

Related

Order by and group by and sum using SQL

What I am trying to do is get the top 10 most sold Vegetables by grouping them by an Id passed by parameter in a function and ordering them by the sum of their Quantity. I don't know how to use SUM or (total) quite yet but I thought I'd post it here seeking help. If you need me offering you anything else I will be ready.
This is my code:
TheVegLinQDataContext db = new TheVegLinQDataContext();
var query =db.OrderDetails.GroupBy(p => p.VegID)
.Select(g => g.OrderByDescending(p => p.Quantity)
.FirstOrDefault()).Take(10);
And this is an image of my database diagram
Group orders by Vegetable ID, then from each group select data you want and total quantity:
var query = db.OrderDetails
.GroupBy(od => od.VegID)
.Select(g => new {
VegID = g.Key,
Vegetable = g.First().Vegetable, // if you have navigation property
Total = g.Sum(od => od.Quantity)
})
.OrderByDescending(x => x.Total)
.Select(x => x.Vegetable) // remove if you want totals
.Take(10);
Since this is not clear that you are passing what type of id as function parameter, I'm assuming you are passing orderId as parameter.
First apply where conditions then group the result set after that order by Total sold Quantity then apply Take
LINQ query
var result = (from a in orderdetails
where a.OrderId == orderId //apply where condition as per your needs
group a by new { a.VegId } into group1
select new
{
group1.Key.VegId,
TotalQuantity = group1.Sum(x => x.Quantity),
group1.FirstOrDefault().Vegitable
}).OrderByDescending(a => a.TotalQuantity).Take(10);
Lamda (Method) Syntax
var result1 = orderdetails
//.Where(a => a.OrderId == 1) or just remove where if you don't need to filter
.GroupBy(x => x.VegId)
.Select(x => new
{
VegId = x.Key,
x.FirstOrDefault().Vegitable,
TotalQuantity = x.Sum(a => a.Quantity)
}).OrderByDescending(x => x.TotalQuantity).Take(10);

Group inside group linq

I have a datatable like this:
I want to group this table For FIELD A and FIELD B,
and the third field of my group should be lists of FIELD C, but it must be grouped by ID field.
At the end, the result should be like this:
First Field | Second Field | Third Field
------------+--------------+----------------
5 | XXXX |(8) (2,6,3) (9)
5 | KKKK |(8,3)
The third field must be a list of lists.
How can i do this with LINQ?
I tried this so far:
var trytogroup = (from p in datatable.AsEnumerable()
group p by new
{
ID = p["Id"].ToLong(),
FieldA = p["FieldA"].ToLong(),
FieldB = p["FieldB"].ToString()
} into g
select new
{
FirstField = g.Key.FieldA,
SecondField = g.Key.FieldB,
ThirdField = datatable.AsEnumerable().Where(p => p["FieldA"].ToLong() == g.Key.FieldA && p["FieldB"].ToString() == g.Key.FieldB).Select(p => p["FieldC"].ToLong()).GroupBy(x => x["Id"].ToLong()).Distinct().ToList()
});
What's wrong with your query:
You don't need to group by three fields on first place. Grouping by ID should be done within group which you have by FieldA and FieldB
When getting ThirdField you don't need to query datatable again - you already have all required data. You just need to add grouping by ID
Correct query:
from r in datatable.AsEnumerable()
group r by new {
FieldA = r.Field<long>("FieldA"),
FieldB = r.Field<string>("FieldB")
} into g
select new
{
First = g.Key.FieldA,
Second = g.Key.FieldB,
Third = g.GroupBy(r => r.Field<long>("ID"))
.Select(idGroup => idGroup.Select(i => i.Field<long>("FieldC")).ToList())
}
If you prefere lambdas, your query could look like:
dataSource
.GroupBy(item => new { item.FieldA, item.FieldB })
.Select(group => new
{
First = group.Key.FieldA,
Second = group.Key.FieldB,
Third = group.GroupBy(q => q.Id).Select(q => q.Select(e => e.FieldC).ToArray()).ToArray()
}).ToArray();
Just few small notes. .GroupBy uses Lookup to get the Groupings, so some overhead can be avoided by replacing .GroupBy( with .ToLookup( when deffered execution is not needed.
The elements in each Grouping are stored in array, so I don't see much use in converting them .ToList (but you can save a bit of space if you convert them .ToArray).
DataTable.AsEnumerable uses .Rows.Cast<TRow>(), but also seems to do some extra stuff when there is any DataView sorting or filtering that are usually not needed.
var groups = datatable.Rows.Cast<DataRow>()
.ToLookup(r => Tuple.Create(
r["FieldA"] as long?,
r["FieldB"]?.ToString()
))
.Select(g => Tuple.Create(
g.Key.Item1,
g.Key.Item2,
g.ToLookup(r => r["ID"] as long?, r => r["FieldC"] as long?)
)).ToList();
As usual, premature optimization is the root of all evil but I thought the information might be useful.

How to c# List<> order by and Group by according to parameters?

I have a class and its List
abc cs = new abc();
List<abc> Lst_CS = new List<abc>();
and I set some value by HidenField in foreach loop
foreach (blah blah)
{
cs = new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
};
Lst_CS.Add(cs);
}
and finally I get a List_CS and I order by Lst_CS according to Date like this;
IEnumerable<abc> query = Lst_CS.OrderBy(l => l.Date).ToList();
but in extra, I want to group by according to No.
Briefly, I want to order by Date and then group by No on Lst_CS How can I do ?
Thanks for your answer
Well you just just do the ordering then the grouping like so:
Lst_CS.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();
Each list of items in each group will be ordered by date. The groupings will be in the order that they are found when the entire list is ordered by date.
Also your ForEach can be done in one Linq statement, then combined with the ordering and grouping:
var query = blah.Select(b => new abc{
No = VKNT,
GuidID=hdnGuidID.Value.ToString(),
RecID=hdnRecID.Value.ToString(),
Date=HdnDate.Value.ToString()
})
.OrderBy(l => l.Date)
.GroupBy(l => l.No)
.ToList();

Find MAX/MIN list item using LINQ?

I have a list Having multiple Items and 3 props ID,DATE,COMMENT.ID field is Auto incremented in DATABASE.
Let say list Contains
2,16AUG,CommentMODIFIED
1,15AUG,CommentFIRST
3,18AUG,CommentLASTModified
I want to get a single ITEM.Item Having Minimum DATE and having Latest Comment. In this case
1,15AUG,CommentLASTModified
Any easy way to do it using LINQ.
orderedItems = items.OrderBy(x => x.Date);
var result = items.First();
result.Comment = items.Last().Comment;
To get a single item out of the list, you can order the items then take the first one, like this:
var result = items
.OrderByDescending(x => x.Date)
.First();
But First will throw an exception if the items collection is empty. This is a bit safer:
var result = items
.OrderByDescending(x => x.Date)
.FirstOrDefault();
To get the min / max of different columns you can do this:
var result =
new Item {
Id = 1,
Date = items.Min(x => x.Date),
Comment = items.Max(x => x.Comment)
};
But this will require two trips to the database. This might be a bit more efficient:
var result =
(from x in items
group x by 1 into g
select new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();
Or in fluent syntax:
var result = items
.GroupBy(x => 1)
.Select(g => new Item {
Id = 1,
Date = g.Min(g => g.Date),
Comment = g.Max(g => g.Comment)
})
.First();

Use Linq to return first result for each category

I have a class (ApplicationHistory) with 3 properties:
ApplicantId, ProviderId, ApplicationDate
I return the data from the database into a list, however this contains duplicate ApplicantId/ProviderId keys.
I want to supress the list so that the list only contains the the earliest Application Date for each ApplicantId/ProviderId.
The example below is where I'm currently at, but I'm not sure how to ensure the earliest date is returned.
var supressed = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.First();
All advice appreciated.
Recall that each group formed by the GroupBy call is an IGrouping<ApplicationHistory>, which implements IEnumerable<ApplicationHistory>. Read more about IGrouping here. You can order those and pick the first one:
var oldestPerGroup = history
.GroupBy(x => new
{
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).FirstOrDefault());
You are selecting first group. Instead select first item from each group:
var supressed = history
.GroupBy(x => new {
ApplicantId = x.ApplicantId,
ProviderId = x.ProviderId
})
.Select(g => g.OrderBy(x => x.ApplicationDate).First());
Or query syntax (btw you don't need to specify names for anonymous object properties in this case):
var supressed = from h in history
group h by new {
h.ApplicantId,
h.ProviderId
} into g
select g.OrderBy(x => x.ApplicationDate).First();

Categories