Not really sure how to word it but basically I have a bunch of data in a list that goes similar to this:
Person 1
Name - Joe Bloggs
Age - 40
Department - IT
Wage - 20,000
Person 2
Name - Jess Jane
Age - 40
Department - Kitchen
Wage - 16,000
...you get the idea.
At the moment, I've just selected all of the people, ordered them by wage and entered them in a listbox very simply by doing this.
var item = (from employee in employeeList.employees
orderby employee.wage descending
select employee);
Now, my question is, how can I change this bit of code so that it filters through the list and show only the highest earning employee in their department? So for example, instead of having hundreds of employees listed, it will only show the highest earning employee in IT, then the highest earning employee in catering, etc.
Is it possible? If not are there any other methods I can use to achieve this?
You could try something like this:
var result = employeeList.employees.GroupBy(emp => emp.Departemnt)
.Select(gr => new
{
Departemnt = gr.Key,
Employee = gr.OrderByDescending(x=>x.wage)
.FirstOrDefault()
});
That we do above is a grouping by department and the we pick for each department the employee with the highest wage.
Update
In .NET 6, the above could be re-written as below:
var result = employeeList.employees
.GroupBy(employee => employee.Departemnt)
.Select(gr => new
{
Departemnt = gr.Key,
Employee = gr.MaxBy(x => x.wage)
});
This approach lets you get the result without ordering. So it will take only O(n) time instead of O(n*log(n))
var highestEarningPersonByDepartment = persons
.GroupBy(p => p.Department)
.Select(g => new { Department = g.Key, HighestEarningPerson = g.First(person => person.Wage == g.Max(p => p.Wage)) })
.ToDictionary(dp => dp.Department, dp => dp.HighestEarningPerson);
Edit: A more optimised version would be:
var maxWageByDepartment = persons
.GroupBy(p => p.Department)
.ToDictionary(g => g.Key, g => g.Max(p => p.Wage));
var richestPersonByDepartment = persons
.GroupBy(p => p.Department)
.Select(g => new { Department = g.Key, HighestEarningPerson = g.First(person => person.Wage == maxWageByDepartment[g.Key]) });
Something along these line :
var highSalemployeePerDept= employeeList.employees.GroupBy(x => x.Department)
.Select(g => g.OrderByDescending(x => x.Salary).First());
var highestEarnersByDept =
employeeList.employees.GroupBy(employee => employee.Department)
.Select(grouping => grouping.OrderByDescending(employee => employee.Wage).First())
.ToArray();
Related
My tables here :
In C#
I want to know most repeated 3 categoryName and their count in Blog table, any idea? Thanks
var result = blogs.GroupBy(b => b.CategoryID)
.OrderByDescending(g => g.Count())
.Take(3)
.Select(x => new {CategoryName = x.First().Category.CategoryName, Count = x.Count()})
.ToList();
This will group your blogs by CategoryID, order by count of each grouping, take the top 3 and then select the category name and count of each group as a list.
list.GroupBy(x => x.CategoryName).Select(x => new { x.Key, count = x.Count() }).OrderBy(x => x.count).Take(3);
This will first Group the items by the name.
Then create an anonymous object with the group key (the name) and the Count of all items in every group. Then you order by count and take the first 3.
You could group by category name, order by highest count first and pick the first three results. Example:
Blogs
.GroupBy(b => b.Category.CategoryName)
.OrderByDescending(g => g.Count())
.Take(3)
.Select(x => new { CategoryName = x.Key, Count = x.Count() });
I'm trying to make a linq using where, group by and select at same time but I cannot do it works and it always throws an exception.
How could I do it works ?
Linq
public ActionResult getParceiros(){
//return all partners
IList<ViewParceirosModel> lista = new List<ViewParceirosModel>();
lista = context.usuarios.Where(u => u.perfil == RoleType.Parceiro)
.Select(x => new ViewParceirosModel
{
id = x.id,
nomeParceiro = x.nome,
emailAcesso = x.email
})
.GroupBy(x => x.id)
.OrderBy(x => x.nomeParceiro)
.ToList();
return View(lista);
}
Exception
Your program doesn't do what you want. Alas you forgot to tell you what you want, you only showed us what you didn't want. We'll have to guess.
So you have a sequence of Usarios.
IQueryable<Usario> usarios = ...
I don't need to know what a Usario is, all I need to know is that it has certain properties.
Your first step is throwing away some Usarios using Where: you only want to keep thos usarios that have a Perfil equal to RoleType.Parceirdo:
// keep only the Usarios with the Perfil equal to RoleType.Parceirdo:
var result = usarios.Where(usario => usario.Perfil == RoleType.Parceirdo)
in words: from the sequence of Usarios keep only those Usarios that have a Perfil equal to RoleTyoe.Parceirdo.
The result is a subset of Usarios, it is a sequence of Usarios.
From every Usario in this result, you want to Select some properties and put them into one ViewParceirosModel:
var result = usarios.Where(usario => usario.Perfil == RoleType.Parceirdo)
.Select(usario => new ViewParceirosModel
{
Id = x.id,
NomeParceiro = x.nome,
EmailAcesso = x.email,
})
In words: from every Usario that was kept after your Where, take the Id, the Nome and the Email to make one new ViewParceirosModel.
The result is a sequence of ViewParceirosModels. If you add ToList(), you can assign the result to your variable lists.
However your GroupBy spoils the fun
I don't know what you planned to do, but your GroupBy, changes your sequence of ViewParceirosModels into a sequence of "groups of ViewParceirosModels" Every ViewParceirosModel in one group has the same Id, the value of this Id is in the Key.
So if after the GroupBy you have a group of ViewParceirosModel with a Key == 1, then you know that every ViewParceirosModel in this group will have an Id equal to 1.
Similarly all ViewParceirosModel in the group with Key 17, will have an Id equal to 17.
I think Id is your primary key, so there will only be one element in each group. Group 1 will have the one and only ViewParceirosModel with Id == 1, and Group 17 will have the one and only ViewParceirosModel with Id == 17.
If Id is unique, then GroupBy is useless.
After the GroupBy you want to Order your sequence of ViewParceirosModels in ascending NomeParceiro.
Requirement
I have a sequence of Usarios. I only want to keep those Usarios with a Perfil value equal to RoleType.Parceirdo. From the remaining Usarios, I want to use the values of properties Id / Nome / Email to make ViewParceirosModels. The remaining sequence of ViewParceirosModels should be ordered by NomeParceiro, and the result should be put in a List.
List<ViewParceirosModel> viewParceiroModels = Usarios
.Where(usario => usario.Perfil == RoleType.Parceirdo)
.Select(usario => new ViewParceirosModel
{
Id = x.id,
NomeParceiro = x.nome,
EmailAcesso = x.email,
}
.OrderBy(viewParceirosModel => viewParceirosModel.NomeParceiro)
.ToList();
When you create a LINQ query with group by clause, you receive as result a grouped query.
It is a kind of dictionary that has as key the field you chose to group and as value a list of records of this group.
So, you cannot order by "nomeParceiro" because this field is inside the group.
If you detail how you expect the result I can show you a code example for this.
You can find more details in this section of the doc: https://learn.microsoft.com/pt-br/dotnet/csharp/linq/group-query-results
Let's say ViewParceirosModel look like
public class ViewParceirosModel
{
public int id {get; set;}
public List<string> nomeParceiro {get; set;}
public List<string> emailAcesso {get; set;}
}
After that, you can Groupby then select combine with Orderby like below
IList<ViewParceirosModel> lista = new List<ViewParceirosModel>();
lista = context.usuarios.Where(u => u.perfil == RoleType.Parceiro)
.Select(x => new ViewParceirosModel
{
id = x.id,
nomeParceiro = x.nome,
emailAcesso = x.email
})
.GroupBy(x => x.id)
.Select(g => new ViewParceirosModel
{
id = g.Key,
nomeParceiro = g.Select(p => p.nomeParceiro).OrderBy(x => x.nomeParceiro).ToList()
nomeParceiro = g.Select(p => p.emailAcesso).ToList()
})
.ToList();
You can use the following code.
IList<ViewParceirosModel> lista = new List<ViewParceirosModel>();
lista = context.usuarios.Where(u => u.perfil == RoleType.Parceiro)
.Select(x => new ViewParceirosModel
{
id = x.id,
nomeParceiro = x.nome,
emailAcesso = x.email
})
.OrderBy(x => x.nomeParceiro)
.GroupBy(x => x.id)
.ToList();
or
List<List<ViewParceirosModel>> listb = context.usuarios
.Where(u => u.perfil == RoleType.Parceiro)
.GroupBy(g => g.id).OrderBy(g => g.Key)
.Select(g => g.OrderBy(x => x.nomeParceiro)).ToList();
I have a list where I'm applying the following condition with linQ:
I want to select all items where Name contains a certain string.
var nameFilter = result
.Where(e => e.Name.Contains(requestedValue))
.ToList();
At the end, sometimes it happens that I am having a list with repeated names:
For example:
requestedValue = 'form';
I end up with:
Name Price
transformer 100
transformer 20
formation 340
former 201
I got transformer twice. In that case, I want to only leave transformer with the least price : 20
How could I do this with linQ without looping?
You can take advantage of GroupBy method
var nameFilter = result.Where(e => e.Name.Contains(requestedValue))
.GroupBy(k=>k.Name, g=>g.Price, (k,g)=>new Model {Name = k, Price = g.Min()})
.ToList();
where new Model should be changed to your class name.
If you have more properties to return probably it will be more convenient to do
var nameFilter = result.Where(e => e.Name.Contains(requestedValue))
.GroupBy(k => k.Name, g => g, (k, g) =>
{
var minPrice = g.Min(x => x.Price);
return g.First(x => x.Price == minPrice);
}).ToList();
Finding minPrice and finding the item with minPrice can be done is a single for loop or, for example, by using following discussion here
So after what felt like a lot of head banging, I have this query:
var widgets = db.Updates
.Where(c => c.Sold.Equals(false))
.GroupBy(c => c.widgetType)
.Select(x => x.OrderByDescending(y => y.TimeStamp).First()).ToList();
widgetGrid.DataSource = widgets;
widgetGrid.DataBind();
Now that I have all the sold widgets I need to add a join, let's say for instance that I want to join the the "Owner" table on ID in Owner equals ID in Widgets and then select Owner.Name and Widget.Style
For the life of me, I seem to be getting nowhere fast... anyone?
As always... I'm deeply grateful for anyone's time in helping me clear out my cobwebs.
If I understand you correctly, you have two sequences:
a sequence of Widgets, where each widget has a property Id.
You have a sequence of Owners, where each owner has a property Id
And you want the combination of sequences and owners that have a matching Id.
By the way, probably your widget will have an ownerId or your owner will have a widgetId, but that won't influence the solution.
The join will be as follows:
var joinedTable = widgets.join(owners, // join table widgets with table owners
w => w.Id // from widgets take the Id
o => o.Id // from owners also take the Id
(widget, owner) => new // where those Ids match, take the owner and the widget
{ // and take the properties you want
Id = widget.Id,
MyXProperty = owner.X,
MyYProperty = widget.Y,
Widget = widget, // or take the complete widget and owner
Owner = owner,
});
By the way, you write "Now that I have all the sold widgets". From your piece of code I understand that each Update has a Boolean property Sold and that you want all Updates where !Sold. I'd assume you end up with items that are not sold?
What is the advantage of your predicate in the where clause. Why isn't it:
var widgets = db.Updates.Where(c => !c.Sold)
.GroupBy // etc.
You could do this:
var widgets = db.Updates
.Where(c => !c.Sold)
.GroupBy(c => c.widgetType)
.Select(x => x.OrderByDescending(y => y.TimeStamp).FirstOrDefault());
var result= (from w in widgets
join o in db.Owners on w.OwnerId equals o.Id
select new {o.Name, w.Style}).ToList();
you may also try:
var widgets = db.Updates
.Where(c => c.Sold.Equals(false))
.GroupBy(c => c.widgetType)
.Select(x => x.OrderByDescending(y => y.TimeStamp).First())
.Join( db.Owners,
u => u.ID,
o => o.ID,
(u, o) => new { o.Name, u.Style }).ToList();
I am new to LINQ and trying to build a website with MVC and LINQ. I want to display top 5 products with the most views. I have 2 tables like below, I am explaining as simple as possible.
PRODUCT
-------
ID
NAME
PRODUCT_VIEWS_TABLE
-------
ID
PRODUCT_ID
Everytime a product is viewed, I insert a new row to the PRODUCT_VIEWS_TABLE. How can I write the LINQ query for this?
(from c in db.PRODUCT select c).Take(5)
How about this:
var top5 = productViews
.GroupBy(view => view.ProductId) // group views by product
.OrderByDescending(g => g.Count()) // order from most- to least-viewed
.Take(5) // take the top 5
.Select(g => g.First().Product); // fetch the corresponding product
var topProductsIds = db.PRODUCT_VIEWS_TABLE // table with a row for each view of a product
.GroupBy(x => x.PRODUCT_ID) //group all rows with same product id together
.OrderByDescending(g => g.Count()) // move products with highest views to the top
.Take(5) // take top 5
.Select(x => x.Key) // get id of products
.ToList(); // execute query and convert it to a list
var topProducts = db.PRODUCTS // table with products information
.Where(x=> topProductsIds.Contains(x.ID)); // get info of products that their Ids are retrieved in previous query
Try this:
var topProducts = m_context.PRODUCTS
.Join(m_context.PRODUCT_VIEW, product=> product.Id, view => view.ProductId,
(product, view ) => new { ProductId = product.Id, ViewId = view.Id})
.GroupBy(g => g.ProductId)
.Select(s => new {
Id = s.Key,
ViewCount= s.Count()
})
.OrderByDescending(o => o.ViewCount)
.Take(5).ToList();