This question already has answers here:
Multiple "order by" in LINQ
(7 answers)
Closed 8 years ago.
For example, I have an object with 3 properties: Name, Price and Description. I need to sort a collection of these objects. It's easy to sort by only one parameter:
var sortedList = ObjectCollection.OrderBy(x => x.Name).ToList();
But how to perform the sorting by 2 parameters (Name and Price). For example, I need to get a list like that:
ItemName1 $100 SomeDescription
ItemName1 $200 AnotherDescription
ItemName1 $250 AnotherDescription
ItemName2 $20 AnotherDescription
ItemName2 $40 Description
ItemName3 $100 Description
and so on. So, the main key is a Name, but if there are several items with the same name then the second key is the price. How to do this?
Use the ThenBy extension, as it does not override previously used OrderBy calls.
var sortedList = ObjectCollection.OrderBy(x => x.Name).ThenBy(x => x.Price).ToList();
You need to use ThenBy() using linq
ObjectCollection.OrderBy(x => x.Name).ThenBy(x => x.Price).ToList();
Use Enumerable.ThenBy:
var sortedList = ObjectCollection.OrderBy(x => x.Name).ThenBy(x => x.Description).ToList();
var sortedList = ObjectCollection.OrderBy(x => x.Name).ThenBy(x => x.SecondPropertyToOrderBy).ToList();
You can chain the OrderBy or OrderByDescending like this. I Hope this helps
You can use Linq:
ObjectCollection.OrderBy(x => x.Name).
ThenBy(x => x.Price).ToList();
If you can't use Linq, you can write a custom comparator.
Related
This question already has answers here:
How to get first record in each group using Linq
(7 answers)
Closed 1 year ago.
What I try to achieve is the following: Filter the ProjektStatus with the where function, the group them by ProjektId. Order the group descending by StatusMonatJahr and then take the first element of each group and return it.
public IEnumerable<ProjektStatus> GetCurrentProjektStatus(Func<ProjektStatus, bool> where)
{
return this.db.ProjektStatus
.Where(where)
.GroupBy(x => x.ProjektId)
.Select(x =>
x.OrderByDescending(y => y.StatusMonatJahr))
.First();
}
Unfortunately, with this query I get the first whole group instead of the first element of each group.
What do I have to change to achieve this?
Thanks in advance
The code should return the first element in each group, not the content of the first group.
public IEnumerable<ProjektStatus> GetCurrentProjektStatus(Func<ProjektStatus, bool> where)
{
return this.db.ProjektStatus
.Where(where)
.GroupBy(x => x.ProjektId)
.Select(x => x.OrderByDescending(y => y.StatusMonatJahr).First());
}
Some remarks :
Language keyword should be avoided as variable name. Here it's about the where parameter. whereFunc is a good name.
The GroupBy, Select, OrderByDescending operations can be done remotely (server side), for that they should be called first. An other option is to do everything remotely, for that, the type of the whereFunc should be Expression<Func<ProjectStatus, bool>>.
Personal opinion: you should prefer to code in English, if your company doesn't do it, I feel bad for you.
Here is the result :
public IEnumerable<ProjectStatus> GetCurrentProjectStatuses(Func<ProjectStatus, bool> whereFunc)
{
return ProjectStatuses
.GroupBy(s => s.ProjectId)
.Select(g => g.OrderByDescending(s => s.MonthAndYear).First())
.AsEnumerable() // From now on the execution is done locally
.Where(whereFunc);
}
This question already has answers here:
Scalable Contains method for LINQ against a SQL backend
(5 answers)
Closed 3 years ago.
I have a list of int and I need to select all record from my query where the id is contained in the second list as showed below:
//my list of ids
var ids=[myquery].select(x=> x.id)
query = query.Where(x => ids.Contains(x.Id));
Now LINQ will convert the above in :
SELECT *
FROM [MyTable]
WHERE ([x].[id] IN (108,687, 689, 691, 694, 705, 703,.....)
Now the ids list will grow a lot and I guess this will ruin the performances.
What would be a better solution considering the the ids list will contain more than 200K item ?
It depends on your model, but you should probably use a navigation property.
Given that you currently have something like this:
var ids =
context
.Entity1
.Where(x => x.Property == value)
.Select(x => x.ID)
.ToHashSet();
var items =
context
.Entity2
.Where(x => ids.Contains(x.ID))
.ToList();
write something like this instead:
var items =
context
.Entity2
.Where(x => x.Entity1.Property == value)
.ToList();
You may need to add such an association to your model before this will be possible.
I would suggest to create a stored procedure as:
there is no unnecessary calls between C# and database(e.g., you are collecting these 200K ids)
less code in C#. So your code will be cleaner and clearer
performance is better as sometimes EF generates inefficient SQL code
So calling stored procedure would look like this:
var user = "johndoe";
var blogs = context.Blogs
.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser {0}", user)
.ToList();
OR try to use NOT IN operator if items are less 200K:
query = query.Where(x => !ids.Contains(x.Id));
SQL:
SELECT *
FROM [MyTable]
WHERE ([x].[id] NOT IN (108,687, 689, 691, 694, 705, 703,.....)
This question already has answers here:
Linq version of SQL "IN" statement
(6 answers)
Closed 4 years ago.
[Sorry if it is a duplicate]
I couldn't find properly solution, so I decided to ask a question.
I have an object companies which returns list of elements.
I would like to write a query which will select these all CompanyId which we have in our list. I don't want to select only one record by using FirstOrDefault().
Example:
var companiesSummary = _context.Company
.Where(c => c.CompanyId == companies.Select(cs => cs.ID))
.Include(country => country.Country)
How can I cope with it? Do you have any ideas?
Select the ids of the companies from your in-memory list and then pass that into the query in the where method:
var ids = companies.Select(cs => cs.ID).ToList();
var companiesSummary =
_context.Company
.Where(c => ids.contains(c.ID))
.Include(country => country.Country)
Assuming your companies contains a list of objects with an ID property you want to compare to Company.CompanyId, your query should look like
int[] ids = companies.Select(cs => cs.ID).ToArray();
var companiesSummary = _context.Company
.Where(c => ids.Contains(c.CompanyId))
.Include(company => company.Country);
var matchingCompanies = companies.Where(c => companyIds.Contains(c.Id))
Make companyIds a HashSet<T> for an efficient Contains.
I currently have a list containing items which have fields: "name, description, source, title, ...". I want to dump a list from this, but solely unique based on two keys, the name and description. Basically, I don't want items from the list with the same name and the same description, but if they have different names and the same description, then it's fine.
I looked up that using
list.select(x => x.Name).Distinct()
would give me a list with distinct name, but stacking them would violate having one of the unique keys different, and one the same.
I also took a look into hash sets, but I'm completely confused on how that works.
If anyone could help, it would be greatly appreciated.
If you're just looking for all distinct name/description combinations:
list.Select(x => new {x.Name, x.Description}).Distinct();
You can either use the following example taken from HERE:
IEnumerable<Person> filteredList = originalList
.GroupBy(person => person.Name)
.Select(group => group.First());
Or use DistinctBy from MoreLINQ NuGet available HERE
var uniqueData = list.Select(x => new
{
UniqueKey = x.Name + " " + x.Description,
Data = x,
}).GroupBy(x => x.UniqueKey)
.Select(g => g.First().Data)
.ToList();
You should probably use some unique/special character/string instead of " " to make sure UniqueKey is really unique.
I'm trying to do a GroupBy and then OrderBy to a list I have. Here is my code so far:
reportList.GroupBy(x => x.Type).ToDictionary(y=>y.Key, z=>z.OrderBy(a=>a.Lost));
With the help of the last question I asked on linq I think the ToDictionary is probably unneeded, but without it I don't know how to access the inner value.
To be clear, I need to GroupBy the Type property and want the inner groups I get to be OrderBy the Lost property (an integer). I want to know if there is a better, more efficient way or at the least better then what I've done.
An explanation and not just an answer would be very much appreciated.
Yes, there is better approach. Do not use random names (x,y,z,a) for variables:
reportList.GroupBy(r => r.Type)
.ToDictionary(g => g.Key, g => g.OrderBy(r => r.Lost));
You can even use long names to make code more descriptive (depends on context in which you are creating query)
reportList.GroupBy(report => report.Type)
.ToDictionary(group => group.Key,
group => group.OrderBy(report => report.Lost));
Your code does basically the following things:
Group elements by type
Convert the GroupBy result into a dictionary where the values of the dictionary are IEnumerables coming from a call to OrderBy
As far as the code correctness it is perfectly fine IMO, but maybe can be improved in term of efficiency (even if depends on your needs).
In fact, with your code, the values of your dictionary are lazily evaluated each time you enumerate them, resulting in a call to OrderBy method.
Probably you could perform it once and store the result in this way:
var dict = reportList
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z.OrderBy(a => a.Lost).ToList());
// note the ToList call
or in this way:
var dict = reportList.OrderBy(a => a.Lost)
.GroupBy(x => x.Type)
.ToDictionary(y => y.Key, z => z);
// here we order then we group,
// since GroupBy guarantees to preserve the original order
Looks fine to me. If you use an anonymous type instead of a Dictionary, you could probably improve the readability of the code that uses the results of this query.
reportList.GroupBy(r => r.Type)
.Select(g => new { Type = g.Key, Reports = g.OrderBy(r => r.Lost) });