How to Group Multiple Items using GroupBy()?
Consider the following simplified objects and relationships:
public class Job{
int JobId;
String name;
String status;
Discipline disc;
WorkCategory workCat;
}
public class Discipline {
int DisciplineId;
String DisciplineName;
}
public class Workcategory{
int WorkCategoryId;
String WorkCategoryName;
}
public Class Grouping{
Discipline parent;
List<Workcategory> children;
}
The above models a relationship that a Job
is associated with one Discipline
and one WorkCategory
. The Workcategory
is always a child of a specific parent Discipline
(one-to-many relationship). We can assume that the relationship of the assigned Discipline and Workcategory will always be valid.
The issue I am facing is when I am trying to create a Grouping
result object based on filters that are applied to Jobs
. I am not sure If this can be done or if the approach I am taking is even correct. The exact question itself is not clear to me, however the above defines the problem statement.
- Can the design be improved?
- How can I group Jobs by Discipline and Workcategory?
- Do I even need the
Grouping
class?
I have tried the following (this is my first attempt using Linq), but to no success as my understanding is not complete enough. The other alternative is to first get the Discipline
group and loop through the original grouping picking up the related Workcategory
.
var grouping = repository.GetJobsWithActiveStatus()
.GroupBy(x => new {
x.Discipline.DisciplineID,
x.Discipline.DisciplineName,
x.Category.WorkCategoryID,
x.Category.WorkCategoryName
})
.Select(g => new Grouping{
Discipline = new Discipline{
DisciplineID = g.Key.DisciplineID,
Name = g.Key.DisciplineName
},
Categories = ?? // this is where I am lost
}});
Edit: After having posted this, I realized that the inital GroupBy parameters result in one group of item whereas I am looking for Group-SubGroup result.
Edit 2:
To clarify a bit further, I dont want the associated Jobs as part of the result, but rather the Discipline-Workcategory grouping - thus the reason for the Grouping
class
Initial solution based on @Obalix
Edit 7-Mar-2010:
This solution does not work - The GroupBy on the object Discipline will yield a unique gro开发者_如何转开发uping for each object. I think this is due to it being a reference type. Am I correct? I initially accepted this as the answer, however after some head scratching realised that my mock data itself was faulty. The initial questions still remain answered.
var result = repository.GetJobsWithActiveStatus()
.GroupBy(x => x.disc)
.Select(g => new Grouping
{
Discipline = g.Key,
Catergories = g.GroupBy(x=>x.workCat) // <--- The Problem is here, grouping on a reference type
.Select(l=>l.Key) // needed to select the Key's only
.ToList()
});
Here is a description how you can implement an hierarchical grouping mechanism.
A generic way of doing this using LINQ (as shown in the link) is:
var list = new List<Job>();
var groupedList = list.GroupBy(x => x.disc)
.Select(g => new {
Key = g.Key,
Count = g.Count(),
WorkCategoryGroups = g.GroupBy(x => x.workCat)
});
However, the link also describes an alternative way which allows you to do the following:
var groupedList = list.GroupByMany(x => x.disc, x => x.workCat);
Edit: Following Ahmad's comment here is the strongly typed version:
var groupedList = list.GroupBy(x => x.disc)
.Select(g => new Grouping {
parent = g.Key,
children = g.GroupBy(x => x.workCat).ToList()
});
I'd use Linq syntax:
var jobsByDiscipline =
from j in repository.GetJobsWithActiveStatus()
group j by j.Discipline.DisciplineID into g
select new {
DisciplineID = g.Key,
Jobs = g.ToList()
};
var jobsByCategory =
from j in repository.GetJobsWithActiveStatus()
group j by j.Workcategory.WorkcategoryID into g
select new {
WorkcategoryID = g.Key,
Jobs = g.ToList()
};
I find that easier to read than lots of chained methods with lambda-function parameters.
You can get your groupings from this:
var disciplineAndCategory =
from j in repository.GetJobsWithActiveStatus()
group j by j.Discipline.DisciplineID into g
let categories =
from j2 in g
select j2.Workcategory.WorkcategoryID
select new {
DisciplineID = g.Key,
Jobs = categories.Distinct() // each category id once
};
Here's another way that doesn't require the Grouping class.
ILookup<Discipline, Workcategory> result = repository
.GetJobsWithActiveStatus()
.Select(job => new {disc = job.disc, workCat = job.workCat})
.Distinct()
.ToLookup(x => x.disc, x => x.workCat);
This is the solution that works for me.
I first needed to get all discipline groups, then create the associated workcategory lists where the disciplineID are equal.
var result = liveJobs
.GroupBy(x => new {
x.disc.DisciplineID,
x.disc.DisciplineName
})
.Select(g => new Grouping()
{
parent = new Discipline(g.Key.DisciplineID,g.Key.DisciplineName),
children = g.Where(job=>job.disc.DisciplineID == g.Key.DisciplineID) // filter the current job discipline to where the group key disciplines are equal
.Select(j=>j.workCat)
.ToList()
});
精彩评论