C# - How to Order By in an ICollection? - c#

I have an Applications table. An Application can have many ActivityPhases.
I use the following to display the latest ActivityPhase:
a.ApplicationActivityPhas.Where(aap => aap.ActivityPhas.WorkFlowStep == a.ApplicationActivityPhas.Max(x => x.ActivityPhas.WorkFlowStep)).Select(aap => aap.ActivityPhas.ActivityPhase)
Therefore, my Applications table only displays 1 ActivityPhase per Application record.
I have the following line that I am trying to use to order the ActivityPhase table column by:
data.OrderBy(a => a.ApplicationActivityPhas.FirstOrDefault().ActivityPhas.ActivityPhase);
The problem is that it is ordering the values within the ICollection. It does not order the values displayed in the Applications table.

First group by activity phase and select your maximum workflow step. Then select all the application phases that match this maximum workflow step.
a.ApplicationActivityPhas.GroupBy(aap => aap.ActivityPhase)
.Select(x => new { ActivityPhase = x.Key, MaxStep = x.Max(y => y.WorkFlowStep) })
.SelectMany(x => a.ApplicationActivityPhas.Where(y => y.WorkFlowStep == x.MaxStep && y.ActivityPhase == x.y.ActivityPhase))

The reason that my ordering was not working as expected is that the first ActivityPhase value was being used to determine the order, which was not always the correct one.
To grab the correct value, I just added the where clause that I use to display the correct value in my table:
data.OrderBy(a => a.ApplicationActivityPhas.Where(aap => aap.ActivityPhas.WorkFlowStep == a.ApplicationActivityPhas.Max(x => x.ActivityPhas.WorkFlowStep)).FirstOrDefault().ActivityPhas.ActivityPhase);

Related

How to sort something in LINQ based on many dates?

Hello this is a LINQ Query but it doesn't sort properly because four different dates are involved.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
orderby i.English_seen_by_executive_on descending
orderby i.Brief_seen_by_executive_on descending
orderby i.French_seen_by_executive_on descending
orderby i.Russian_seen_by_executive_on descending
select i).ToList();
All i want is that it should somehow combine the four dates and sort them in group not one by one.
For Example, at the moment it sorts all English Reports based on the date that executive has seen it, then Brief Report and So on.
But i want that it should check which one is seen first and so on. For example if the first report which is seen is French, then Brief, then English then Russian, so it should sort it accordingly.
Is it Possible??
You need to have them all in one column. The approach I would do, assuming that the value of the respective cells is null, when you don't want them to show up in the order by:
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select new
{
Date =
(
i.English_seen_by_executive_on != null ? i.English_seen_by_executive_on :
i.Brief_seen_by_executive_on != null ? i.Brief_seen_by_executive_on :
i.French_seen_by_executive_on != null ? i.French_seen_by_executive_on :
i.Russian_seen_by_executive_on
)
}).ToList().OrderBy(a => a.Date);
In the select clause you could add more columns if you whish.
Reference taken from here.
Why not just use .Min() or .Max() on the dates and then .OrderBy() or .OrderByDescending() based on that?
Logic is creating a new Enumerable (here, an array) with the 4 dates for the current line, and calculate the Max/Min of the 4 dates: this results in getting the latest/earliest of the 4. Then order the records based on this value.
var EventReportRemarks = (from i in _context.pm_main_repz
.Include(a => a.PM_Evt_Cat)
.Include(b => b.department)
.Include(c => c.employees)
.Include(d => d.provncs)
where i.department.DepartmentName == "Finance"
select i)
.OrderBy(i => new[]{
i.English_seen_by_executive_on,
i.Brief_seen_by_executive_on,
i.French_seen_by_executive_on,
i.Russian_seen_by_executive_on
}.Max())
.ToList();
Your problem is not a problem if you use method syntax for your LINQ query instead of query syntax.
var EventReportRemarks = _context.pm_main_repz
.Where(rep => rep.Department.DepartmentName == "Finance")
.OrderByDescending(rep => rep.English_seen_by_executive_on)
.ThenByDescending(rep => rep.Brief_seen_by_executive_on)
.ThenByDescending(rep => rep.French_seen_by_executive_on descending)
.ThenByDescending(rep => resp.Russian_seen_by_executive_on descending)
.Select(rep => ...);
Optimization
One of the slower parts of a database query is the transport of selected data from the DBMS to your local process. Hence it is wise to limit the transported data to values you actually plan to use.
You transport way more data than you need to.
For example. Every pm_main_repz (my, you do love to use easy identifiers for your items, don't you?), every pm_main_repz has zero or more Employees. Every Employees belongs to exactly one pm_main_repz using a foreign key like pm_main_repzId.
If you use include to transport pm_main_repz 4 with his 1000 Employees every Employee will have a pm_main_repzId with value 4. You'll transport this value 1001 times, while 1 time would have been enough
Always use Select to select data from the database and Select only the properties you actually plan to use. Only use Include if you plan to update the fetched objects
Consider using a proper Select where you only select the items that you actually plan to use:
.Select(rep => new
{
// only Select the rep properties you actually plan to use:
Id = rep.Id,
Name = rep.Name,
...
Employees = rep.Employees.Select(employee => new
{
// again: select only the properties you plan to use
Id = employee.Id,
Name = employee.Name,
// not needed: foreign key to pm_main_repz
// pm_main_repzId = rep.pm_main_repzId,
})
.ToList(),
Department = new
{
Id = rep.Department,
...
}
// etc for pm_evt_cat and provencs
});

C# LINQ Filter deep nested list

I've a structure based of list containing other list. I need to filter the list based on the value of a property based in the deepest part of the list.
Right now I'm doing this:
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts);
queryable = queryable
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)));
I join the needed tables, then I filter them based on the ActionTypeId based 3 levels below the top list.
First off all, is it possible to do this in 1 step ( include the filtering with the joins ), second of all, the second part is not working as my list gets empty, but I'm certain that actions with that type get values.
Using .NET Core 2.0.3 btw!
To answer your first part, you can do this
queryable = queryable
.Include(x => x.Carriers)
.ThenInclude(c => c.CarrierActions)
.ThenInclude(ca => ca.Action)
.ThenInclude(ac => ac.ActionFacts)
.Where(x => x.Carriers.Any(
carriers => carriers.CarrierActions.Any(
carrieractions =>
carrieractions.Action.ActionTypeId ==
ActionTypeEnum.DosemeterCalculateDeepDose)))
To your second part, it should be working, id check your data, as this is pretty straight forward

C# change from groupby

Suppose there are two properties in Myclass: Date, Symbol
I want to frequently convert between those two properties, but I find that
for List <Myclass> vector
if I use
vector.groupby(o => o.Date).Select(o => o)
the vector is no longer the type of List<IGrouping<string, Myclass>>
And if I want to convert groupby(o => o.Date) to groupby(o => o.Symbol)
I have to use
vector.groupby(o => o.Date).Selectmany(o => o).groupby(o => o.Symbol)
I try to use SortedList<Date, Myclass>, but I am not familiar with SortedList(actually, I don't know what's the difference between SortedList and Groupby).
Is there any effective way to achieve such effect, as I highly depend on the speed of running?
int volDay = 100;
Datetime today = new DateTime(2012, 1, 1);
//choose the effective database used today, that is the symbol with data more than volDay
var todayData = dataBase.Where(o => o.Date <= today).OrderByDescending(o => o.Date)
.GroupBy(o => o.Symbol).Select(o => o.Take(volDay))
.Where(o => o.Count() == volDay).SelectMany(o => o);
//Select symbols we want today
var symbolList = todayData
.Where(o => o.Date == today && o.Eqy_Dvd_Yld_12M > 0))
.OrderByDescending(o => o.CUR_MKT_CAP)
.Take((int)(1.5 * volDay)).Where(o => o.Close > o.DMA10)
.OrderBy(o => o.AnnualizedVolatility10)
.Take(volDay).Select(o => o.Symbol).ToList();
//Select the database again only for the symbols in symbolList
var portfolios = todayData.GroupBy(o => o.Symbol)
.Where(o=>symbolList.Contains(o.Key)).ToList();
This is my real code, dataBase is the total data, and I will run the cycle day by day(here just given a fixed day). The last List portfolios is the final goal I want obtain, you can ignore other properties, which are used for the selections under the collection of Date and Symbol
It may be faster, or at least easier to read, if you performed a .Distinct().
To get distinct Dates:
var distinctDates = vector.Select(o => o.Date).Distinct()
To get distinct Symbols:
var distinctSymbols = vector.Select(o => o.Symbol).Distinct()
I asked what you were trying to accomplish so that I can provide you with a useful answer. Do you need both values together? E.g., the unique set of symbols and dates? You should only need a single group by statement depending on what you are ultimately trying to achieve.
E.g., this question Group By Multiple Columns would be relevant if you want to group by multiple properties and track the two unique pieces of data. a .Distinct() after the grouping should still work.

Most Efficient EF Ordering for Multiple Columns in LocalDb

What is the most efficient way to order a LocalDb table in descending order by four columns? I have a table that tracks a file storage hierarchy. Four folders act like an odometer (one digit for each folder). The table reflects this as a "storage item." I need to find the highest number using all four folders.
Here is the code I am currently using. I am worried that it is not efficient or accurate for a LocalDb database...
public StorageItem GetLastItem()
{
var item = _context.StorageItems.AsNoTracking()
.OrderByDescending(x => x.LevelA) // int
.OrderByDescending(x => x.LevelB) // int
.OrderByDescending(x => x.LevelC) // int
.OrderByDescending(x => x.ItemNumber) // int
.Where(x => !x.AuditDateDeleted.HasValue) // DateTime?
FirstOrDefault();
// Caching logic here
return item;
}
I don't think it'll be inefficient, but chaining a bunch of OrderByDescendings is probably not what you intended to do. Currently, this should generate a SQL ORDER BY clause of ItemNumber DESC, LevelC DESC, LevelB DESC, LevelA DESC. I think you want to use ThenByDescending...
var item = _context.StorageItems.AsNoTracking()
.Where(x => !x.AuditDateDeleted.HasValue)
.OrderByDescending(x => x.LevelA)
.ThenByDescending(x => x.LevelB)
.ThenByDescending(x => x.LevelC)
.ThenByDescending(x => x.ItemNumber)
.FirstOrDefault();
Also moved the where clause higher up, although I think the database should be smart enough to optimize that.

LINQ - Preserve order across objects with .Any()? [duplicate]

This question already has answers here:
List sort based on another list
(3 answers)
Closed 9 years ago.
I am building a search function which needs to return a list ordered by relevance.
IList<ProjectDTO> projects = new List<ProjectDTO>();
projects = GetSomeProjects();
List<ProjectDTO> rawSearchResults = new List<ProjectDTO>();
//<snip> - do the various search functions here and write to the rawSearchResults
//now take the raw list of projects and group them into project number and
//number of search returns.
//we will sort by number of search returns and then last updated date
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.Select(x => new
{
Count = x.Count(),
ProjectNbr = x.Key,
LastUpdated = x.First().UpdatedDateTime
})
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.LastUpdated);
So far so good; the "orderedProjects" variable returns my list in the correct order. However, I need the entire object for the next step. When I try to query back to get the original object type, my results lose their order. In retrospect, this makes sense, but I need to find a way around it.
projects = (from p in projects
where orderedProjects.Any(o => o.ProjectNbr == p.ProjectNbr)
select p).ToList();
Is there a LINQ-friendly method for preserving the order in the above projects query?
I can loop through the orderedProject list and get each item, but that's not very efficient. I can also rebuild the entire object in the original orderedProjects query, but I'd like to avoid that if possible.
You need to do it the other way around:
Query orderedProjects and select the corresponding items from projects:
var projects =
orderedProjects
.Select(o => projects.SingleOrDefault(p => p.ProjectNbr == o.ProjectNbr))
.Where(x => x != null) // This is only necessary if there can be
// ProjectNbrs in orderedProjects that are not in
// projects
.ToList();
You shouldn't use "Select" in the middle there as that operator transforms the object into another type and you say that you need the original object.
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.First().UpdatedDateTime);
Do they come in chronological order or something? Otherwise, I'm pretty sure you want the "ThenByDescending" to be performed on the newest or oldest project update like so:
var orderedProjects = rawSearchResults.GroupBy(x => x.ProjectNbr)
.OrderByDescending(x => x.Count)
.ThenByDescending(x => x.Max(p=>p.UpdatedDateTime));

Categories