Ordered grouping without Dictionary - c#

I am grouping train trips according to the coal mines they service, and then dealing with the each group starting with the most populous.
List<List<Trip>> tripsByMine = trips.GroupBy(x => x.demand.Mine)
.ToDictionary(x => x.Key, x => x.ToList())
.Values.OrderByDescending(x => x.Count)
.ToList();
It seems to me that the ToDictionary call is superfluous because I just want the Values. Is there a shorter way of getting the same result?

Try this
List<List<Trip>> tripsByMine2 = trips.GroupBy(x => x.demand.Mine)
.Select(x => x.ToList())
.OrderByDescending(x => x.Count)
.ToList();

Possible solution:
List<List<Trip>> tripsByMine = trips.GroupBy(x => x.demand.Mine)
.Select(x => new {Key=x.Key, Values=x,Count=x.Count()})
.OrderByDescending(x => x.Count)
.Select(x=>x.Values.ToList())
.ToList();

Related

c# linq filter group by max composite key

I have a linq query:
var reports = await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => new { report.PatientId, RecentDate = DbFunctions.TruncateTime(report.DateCreated) })
.OrderByDescending(g => g.Key)
.ToListAsync();
it returns groups sorted in descending order by the composite key (PatientId, RecentDate).
Keys:
10004, 2021-02-03
10004, 2021-01-01
10004, 2021-02-02
10002, 2021-01-05
10002, 2021-01-06
can I somehow take only the groups with the largest key (PatientId, RecentDate)
i.e groups with most recent dates (In this example, the result should be two groups):
10004, 2021-02-03
10002, 2021-01-06
You need to add a Where after your GroupBy:
.Where(grp =>
grp.OrderByDecending(x => x.Key.RecentDate).First().PatientId == grp.Key.PatientId)
Full query:
var reports = await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => new
{
report.PatientId,
RecentDate = DbFunctions.TruncateTime(report.DateCreated)
})
.Where(grp =>
grp.OrderByDecending(x => x.Key.RecentDate).First().PatientId == grp.Key.PatientId)
.OrderByDescending(g => g.Key)
.ToListAsync();
Maybe It will be useful for you. It works with list of elements, but I've never tried it with EF.
var reports = new List<ShopingPatientReport>() { ... }
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => report.PatientId)
.Select(report => report.GroupBy(x => DbFunctions.TruncateTime(report.DateCreated))
.OrderByDescending(f => f.Key).First()).OrderByDescending(g => g.Key)
.ToList()
Here is a query that works for me:
await dbContex.ShoppingListPatientReports
.Where(report => patientIds.Contains(report.PatientId))
.GroupBy(report => report.PatientId)
.Select(patientShoppingList =>
new
{
patientId = patientShoppingList.Key,
//Grouping reports by created date and take group wiht the most recent date
reports = patientShoppingList.GroupBy(report => DbFunctions.TruncateTime(report.DateCreated))
.OrderByDescending(group => group.Key)
.FirstOrDefault()
})
.SelectMany(g => g.reports.Select(r => r))
.ToListAsync();

Linq includes in nested group by query

I have a relatively complex query below with a few nested group by queries. The problem is that I don't know how I can add includes to any of the group by queries. Is there a way to include subproperties in the sub group queries in EF6?
return db.PatientOrders
.Include(x => x.Patient) // this has no effect
.Where(x => !x.ProcessedOn.HasValue && x.Patient.Home.PharmacyId == pharmacyID)
.GroupBy(x => x.Patient.Home)
.ToDictionary(x => x.Key, x => x
.ToList()
.GroupBy(y => y.Patient.Department)
.ToDictionary(y => y.Key, y => y
.Include(x => x.OrderLines) // this does not compile
.ToList()
.GroupBy(z => z.Patient)
.ToDictionary(z => z.Key, z => z.ToList(), new PatientEqualityComparer()), new HomeDepartmentEqualityComparer()), new HomeEqualityComparer());
I figured out a way to do it but I'm not sure if the solution is any good performance-wise.
// group by reshapes query so previous includes are lost
// solution: flatten after group by then do includes then group by again
return db.PatientOrders
.GroupBy(x => x.Patient.Home) // Group
.SelectMany(g => g.AsEnumerable()) // Ungroup
.Include(x => x.Patient)
.Include(x => x.Patient.Home)
.Include(x => x.Patient.Doctor)
.Include(x => x.Patient.Department)
.Include(x => x.OrderLines)
.Include(x => x.OrderLines.Select(y => y.Product))
.Where(x => !x.ProcessedOn.HasValue && x.Patient.Home.PharmacyId == pharmacyID)
.AsEnumerable() // Switch to LINQ to Objects
.GroupBy(x => x.Patient.Home) // Group again
.ToDictionary(x => x.Key, x => x
.ToList()
.GroupBy(y => y.Patient.Department)
.ToDictionary(y => y.Key, y => y
.ToList()
.GroupBy(z => z.Patient)
.ToDictionary(z => z.Key, z => z.ToList(), new PatientEqualityComparer()), new HomeDepartmentEqualityComparer()), new HomeEqualityComparer());

NHibernate QueryOver and string.format

I am working with QueryOver in NHibernate and I want to customize one property of my projected DTO using the following syntax:
IEnumerable<PersonResponseMessage> persons =
session.QueryOver<PersonEntity>()
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
.Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)).WithAlias(() => dto.DetailsUrl)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>();
Unfortunately NHibernate cannot do this and throws an exception saying that:
Variable P referenced from scope "" is not defined
There are in common two ways. Partially we can move that concat operation on the DB side, as documented here:
16.7. Projection Functions
In this case, we'll use the Projections.Concat:
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
// instead of this
//.Select(p => string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id))
// .WithAlias(() => dto.DetailsUrl)
// use this
.Select(p => Projections.Concat(uriHelper.Root, Projections.Concat, p.Id))
.WithAlias(() => dto.DetailsUrl)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>();
But I would vote for ex-post processing on the Application tier, in C#:
.SelectList(list => list
.Select(p => p.Active).WithAlias(() => dto.Active)
.Select(p => p.Alert).WithAlias(() => dto.Alert)
.Select(p => p.Comments).WithAlias(() => dto.Comments)
// just the ID
.Select(p => p.Id).WithAlias(() => dto.Id)
)
.TransformUsing(Transformers.AliasToBean<PersonResponseMessage>())
.List<PersonResponseMessage>()
// do the concat here, once the data are transformed and in memory
.Select(result =>
{
result.DetailsUrl = string.Format("{0}api/Person/{1}", uriHelper.Root, p.Id)
return result;
});

Return max repeated item in list

List<string> prod = new List<string>();
prod.Add("dfg");
prod.Add("dfg");
prod.Add("ojj");
prod.Add("dfg");
prod.Add("e");
In the above code prod List has item "dfg" repeated thrice(max count)...
I want "dfg" as the output because this item is repeated maximum times.
Can anyone help in this
Not the absolutely most efficient, but it works:
var maxRepeatedItem = prod.GroupBy(x => x)
.OrderByDescending(x => x.Count())
.First().Key;
This is more efficient:
var maxRepeatedItem = prod.GroupBy(x => x)
.MaxBy(x => x.Count())
.First().Key;
but it requires MoreLinq's extension MaxBy
EDIT (as per comment) :
If you want all the max repeated elements in case of ties, here's a possible solution:
var grouped = prod.ToLookup(x => x);
var maxRepetitions = grouped.Max(x => x.Count());
var maxRepeatedItems = grouped.Where(x => x.Count() == maxRepetitions)
.Select(x => x.Key).ToList();
You can use LINQ:
string maxRepeated = prod.GroupBy(s => s)
.OrderByDescending(s => s.Count())
.First().Key;

Why can I not use OrderBy() in this lambda expression?

How do I order the following? The orderBy doesnt recognise the x.Name.
var xRefsNames = db.CrossRefs.Where(x => pgNos.Contains(x.PG))
.Select(x => x.Name)
.Distinct()
.OrderBy(x=>x.Name);
Your select is projecting a different object, probably a string based on the name. You want to just order by x.
var xRefsNames = db.CrossRefs.Where(x => pgNos.Contains(x.PG))
.Select(x => x.Name)
.Distinct()
.OrderBy(x=>x);

Categories