Create GroupBy Statements Dynamically
c#, linq
Solution
An intriguing problem. Choosing a single type for grouping keys and another type for results... makes it is very possible to get what you're asking for.
public struct EntityGroupKey
{
public int ID {get;set;}
public string Name {get;set;}
}
public class EntityGrouper
{
public Func<Entity, EntityGroupKey> KeySelector {get;set;}
public Func<EntityGroupKey, TreeViewItem> ResultSelector {get;set;}
public EntityGrouper NextGrouping {get;set;} //null indicates leaf level
public List<TreeViewItem> GetItems(IEnumerable<Entity> source)
{
var query =
from x in source
group x by KeySelector(x) into g
let subItems = NextGrouping == null ?
new List<TreeViewItem>() :
NextGrouping.GetItems(g)
select new { Item = ResultSelector(g.Key), SubItems = subItems };
List<TreeViewItem> result = new List<TreeViewItem>();
foreach(var queryResult in query)
{
// wire up the subitems
queryResult.Item.SubItems = queryResult.SubItems
result.Add(queryResult.Item);
}
return result;
}
}
Used in this way:
EntityGrouper companyGrouper = new EntityGrouper()
{
KeySelector = o => new EntityGroupKey() {ID = o.CompanyID, Name = o.CompanyName},
ResultSelector = key => new TreeViewItem
{
CompanyID = key.ID,
DisplayName = key.Name,
ItemTypeEnum = TreeViewItemType.Company
}
}
EntityGrouper divisionGrouper = new EntityGrouper()
{
KeySelector = o => new EntityGroupKey() {ID = 0, Name = o.Division},
ResultSelector = key => new TreeViewItem
{
DisplayName = key.Name,
ItemTypeEnum = TreeViewItemType.Division
}
}
companyGrouper.NextGrouping = divisionGrouper;
List<TreeViewItem> oneWay = companyGrouper.GetItems(source);
companyGrouper.NextGrouping = null;
divisionGrouper.NextGrouping = companyGrouper;
List<TreeViewItem> otherWay = divisionGrouper.GetItems(source);
Problem
I am trying to dynamically re-structure some data to be shown in a treeview which will allows the user to select up to three of the following dimensions to group the data by: ``` Organisation Company Site Division Department ``` So for example, if the user were to select that they wanted to group by Company then Site then Division...the following code would perform the required groupings. ``` var entities = orgEntities // Grouping Level 1 .GroupBy(o => new { o.CompanyID, o.CompanyName }) .Select(grp1 => new TreeViewItem { CompanyID = grp1.Key.CompanyID, DisplayName = grp1.Key.CompanyName, ItemTypeEnum = TreeViewItemType.Company, SubItems = grp1 // Grouping Level 2 .GroupBy(o => new { o.SiteID, o.SiteName }) .Select(grp2 => new TreeViewItem { SiteID = grp2.Key.SiteID, DisplayName = grp2.Key.SiteName, ItemTypeEnum = TreeViewItemType.Site, SubItems = grp2 // Grouping Level 3 .GroupBy(o => new { o.Division }) .Select(grp3 => new TreeViewItem { DisplayName = grp3.Key.Division, ItemTypeEnum = TreeViewItemType.Division, }).ToList() }).ToList() }) .ToList(); ``` This would give a structre like this: ``` + Company A + Site A + Division 1 + Division 2 + Site B + Division 1 + Company B + Site C + Division 2 + Company C + Site D ``` However, this only provides me with on of a large number of combinations. How would I go about converting this into something that could create the equivalent expression dynamically based on the three dimensions that the user has chosen and so I don't have to create one of each of these expressions for each combination!!? Thanks guys.