I have a list(mainlist) with the following
PsID Nominprsn
---- ---------
1 James
2 Troy
2 William
3 Mike
4 Jason
4 Hendry
I need to get the first name from a given PsID
so the result would be as such
PsID Nominprsn
---- ---------
1 James
2 Troy
3 Mike
4 Jason
I tried the following but was not successful as I didn't know how to get the first PsID:
var proglis = (from ts in mainlist
group ts by new { ts.PsID, Nominprsn} into grp
select new {grp.Key.PsID grp.Key.Nominprsn}).ToList();
To select the first item from each group use this query:
var query = mainlist.GroupBy(o => o.PsID)
.Select(g => new { PsID = g.Key, g.First().Nominprsn })
.ToList();
This will return the same results you've shown in your question. The result will be an IEnumerable of anonymous types with properties named PsID and Nominprsn.
Here is how you should do it:
var proglis = (from ts in mainlist
group ts by ts.PsID into grp
select new
{
Id = grp.Key,
Name = grp.First().Nominprsn
}).ToList();
Or maybe try Distinct or DistinctBy(o => o.Key.PsID)
Related
I am trying to query a table with nested linq query. My query working but is too slow. I have almost 400k row. And this query work 10 seconds for 1000 rows. For 400k I think its about to 2 hours.
I have rows like this
StudentNumber - DepartmentID
n100 - 1
n100 - 1
n105 - 1
n105 - 2
n107 - 1
I want the students which have different department ID. My results looks like this.
StudentID - List
n105 - 1 2
And my query provides it. But slowly.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste=(from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in YOKAktarim where birim.StudentID == g.Key select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
Console.WriteLine(sorgu.Where (s => s.liste != null).OrderBy (s => s.Key));
I wrote this query with linqpad C# statement.
For 400K records you should be able to return the student ids and department ids into an in-memory list.
var list1 = (from r in YOKAktarim
group r by new { r.StudentID, r.DepartmentID} into g
select g.Key
).ToList();
Once you have this list, you should be able to group by StudentID and select those students who have more than one record.
var list2 = (from r in list1 group r by r.StudentID into g
where g.Count() > 1
select new
{
StudentID = g.Key,
Departments = g.Select(a => a.DepartmentID).ToList()
}
).ToList();
This should be faster as it only hits the sql database once, rather than hundreds of thousands of times.
You're iterating your source collection (YOKAktarim) three times, which makes your query *O(n^3)` query. It's going to be slow.
Instead of going back to source collection to get content of the group you can simply iterate over g.
var sorgu = (from yok in YOKAktarim
group yok by yok.StudentID into g
select new {
g.Key,
liste = from birim in g select new { birim.DepartmentID }).ToList().GroupBy (x => x.DepartmentID).Count()>1 ? (from birim in g select new { birim.DepartmentID }).GroupBy(x => x.DepartmentID).Select(x => x.Key).ToList() : null,
}).Take(1000).ToList();
However, that's still not optimal, because you're doing a lot of redundant subgrouping. Your query is pretty much equivalent to:
from yok in YOKAktarim
group yok by yok.StudentID into g
let departments = g.Select(g => g.DepartmentID).Distinct().ToList()
where departments.Count() > 1
select new {
g.Key,
liste = departments
}).Take(1000).ToList();
I can't speak for the correctness of that monster, but simply removing all ToList() calls except the outermost one will fix your issue.
I am new to LINQ. I am stuck with a very silly problem
Name Subjects Role
---- -------- --------
A Math Student
A English Student
B Math Student
B English Student
C Math Student
C Math Admin
I need result as
Name Subjects Role
---- -------- --------
A Math, English Student
B Math, English Student
C Math Student
C Math Admin
I am confused as to how to go about this problem. This is simple in SQL where I can do a groupby clause and get the comma seperated values via a function.
Can someone please help me out?
Edited: The three columns are from 3 different sources. I have updated the resultant table. Thanks for your help in advance!
I don't have your code but it should look like this:
var grouped = from element in yourList
group element by element.Name into g
select new
{
Name = g.Key,
Subjects = g.Select(e => e.Subject),
// Assuming they are identical when they have the same name
Role = g.First().Role
};
Try this:
var grouped = classes.GroupBy(g => new {Name = g.Name, Role = g.Role}).Select(
s =>
new
{
Name = s.Key,
Subjects = s.Select(x => x.Subject).Aggregate("", (current, se) => current + (", " + se)),
Role = s.Select(x => x.Role).First()
});
var result = grouped.Select(s => new
{
s.Name,
Subjects = s.Subjects.Substring(2),
s.Role
}).ToList();
This will put your subjects in a comma separated string.
Hope this helps.
I have divAssignments that has potential multiple rows by rNI, an official id, according to a compound key of Indictment and booking numbers.
rNI Booking Indictment
12345 954445 10 12345
12345 954445 10 12346
12345 954445 10 12347
So ID has a count of 3 for a single booking number for this rni.
I get lost attempting to generate a count and a group by booking Number:
var moreThen = from dA in divAssignments
select new { dA.rNI, IndictmentCount = dA.indictmentNumber.Count() };
Most of the examples are dealing with static int[] and don't seem to work in my case.
How do I get a group and then a count? If I could put in a having that would be fantastic.
from a t-sql POV I'd use this:
Select rni, bookingNumber, count(*) IndictmentCount
from divAssignments
group by rni, bookingNumber
having count(*) > 0
TIA
How about something like this:
var query = from item in divAssignments
group item by item.rNI into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
If you're interested in grouping by both the rNI and the booking number, I would change it to this:
var query = from item in divAssignements
group item by new { item.rNI, a.Booking } into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count
};
OR
var query = from item in divAssignments
group item by item into grouping
select new
{
Id = grouping.Key,
Count = grouping.Count()
}
and implement IEquatable on the divAssignment object to support equality comparison. The other option if you'd like is to write an IEqualityComparer instance to do the composite key comparison. Your query could then look like:
var query =
divAssignments
.GroupBy(i => i, new MyCustomEqualityComparer())
.Select(i => new { Key = i.Key, Count = i.Count());
var query =
from dA in divAssignments
group dA by new { dA.rNI, dA.bookingNumber };
foreach(var grp in query)
{
Console.WriteLine("rNI={0}, bookingNumber={1} => Count={2}", grp.Key.rNI, grp.Key.bookingNumber, grp.Count());
}
If you use a Grouping operator in Linq you will get what you need. The code:
var count = from a in divAssignments
group a by new { a.rNI, a.Booking } into b
select b;
will return a collection of IGrouping objects. This will give you the Key (in my example this will be an anonymous type with an rNI and a Booking property) and a collection of the divAssignments that match the key.
Using Method syntax (much easier to read in my opinion):
First group the records, then select a new result for each group that contains the count.
var groups = divAssignments.GroupBy(d => new { d.rNI, d.Booking });
groups.Select(g=> new { g.Key.rNI, g.Key.Booking, IndictmentCount = g.Count() });
I've got a class like the following:
public class Invoice
{
public int InvoiceId {get;set;}
public int VersionId {get;set;}
}
Each time an Invoice is modified, the VersionId gets incremented, but the InvoiceId remains the same. So given an IEnumerable<Invoice> which has the following results:
InvoiceId VersionId
1 1
1 2
1 3
2 1
2 2
How can I get just the results:
InvoiceId VersionId
1 3
2 2
I.e. I want just the Invoices from the results which have the latest VersionId. I can easily do this in T-SQL, but cannot for the life of me work out the correct LINQ syntax. I'm using Entity Framework 4 Code First.
Order by the VersionId, group them by InvoiceId, then take the first result of each group. Try this:
var query = list.OrderByDescending(i => i.VersionId)
.GroupBy(i => i.InvoiceId)
.Select(g => g.First());
EDIT: how about this approach using Max?
var query = list.GroupBy(i => i.InvoiceId)
.Select(g => g.Single(i => i.VersionId == g.Max(o => o.VersionId)));
Try using FirstOrDefault or SingleOrDefault in place of Single as well... it would give the same result although Single shows the intention better.
EDIT: I've tested both these queries with LINQ to Entities. They seem to work, so perhaps the issue is something else?
Option 1:
var latestInvoices = invoices.GroupBy(i => i.InvoiceId)
.Select(group => group.OrderByDescending(i => i.VersionId)
.FirstOrDefault());
EDIT: Changed 'Last' to 'FirstOrDefault', LINQ to Entities has issues with the 'Last' query operator.
Option 2:
var invoices = from invoice in dc.Invoices
group invoice by invoice.InvoiceId into invoiceGroup
let maxVersion = invoiceGroup.Max(i => i.VersionId)
from candidate in invoiceGroup
where candidate.VersionId == maxVersion
select candidate;
My version:
var h = from i in Invoices
group i.VersionId by i.InvoiceId into grouping
select new {InvoiceId = grouping.Key, VersionId = grouping.Max()};
Update
As was mentioned by Ahmad in the comments, the above query will return a projection. The version below will return a IQueryable<Invoice>. I use composition to build the query because I think it is more clear.
var maxVersions = from i in Invoices
group i.VersionId by i.InvoiceId into grouping
select new {InvoiceId = grouping.Key,
VersionId = grouping.Max()};
var latestInvoices = from i in Invoices
join m in maxVersions
on new {i.InvoiceId, i.VersionId} equals
new {m.InvoiceId, m.VersionId}
select i;
I'm quite new to LINQ.
Suppose that I had the following table:
Incident
ID DeviceID Time Info
1 1 5/2/2009 d
2 2 5/3/2009 c
3 2 5/4/2009 b
4 1 5/5/2009 a
In LINQ, how could I write a query that finds the most recent and distinct (on Device ID) set of incidents? The result I'd like is this:
ID DeviceID Time Info
3 2 5/4/2009 b
4 1 5/5/2009 a
Do you have to create an IEqualityComparer to do this?
You can get the most recent incidents for each device (this is how I understood your question) with:
var query =
incidents.GroupBy(incident => incident.DeviceID)
.Select(g => g.OrderByDescending(incident => incident.Time).First())
.OrderBy(i => i.Time); // only add if you need results sorted
int filterDeviceID = 10;
var incidents = (from incident in incidentlist
where incident.DeviceID == filterDeviceID
select incident).Distinct().OrderBy( x => x.Time);