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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With