I have a enumerable list that contains a flattened parent-child relationship:
ParentGuid1, ParentName1, ChildGuid1, ChildName1
ParentGuid1, ParentName1, ChildGuid2, ChildName2
ParentGuid2, ParentName2, ChildGuid3, ChildName3
ParentGuid2, ParentName2, ChildGuid4, ChildName4
I have defined a Child class and a Parent class that includes a List<Child>
property called Children.
Can I use linq to create on object graph with one instance of the Parent class per unique ParentGuid, referencing a List populated by the children associated with that parent.
Somet开发者_C百科hing along the lines of this (note, this code doesn't compile):
myFlattenedHierarchy.Select(p => new Parent
{Guid = p.ParentGuid,
Name = p.ParentName,
Children = myFlattenedHierarchy.Where(c => c.ParentGuid == p.ParentGuid).Select(c => new Child{Guid = c.ChildGuid, Name = c.ChildName})
});
myFlattenedHierarchy.Select(p => new Parent
{Guid = p.ParentGuid,
Name = p.ParentName,
Children = myFlattenedHierarchy.Where(c => c.ParentGuid == p.ParentGuid).Select(c => new Child{Guid = c.ChildGuid, Name = c.ChildName})
});
You should be able to do that, but the Children
can not be a List, it has to be IEnumerable
.
Here's the pre-Linq way to do it with a simple loop.
Dictionary<Guid, Parent> parents = new Dictionary<Guid, Parent>();
foreach(RowType row in myFlattenedHierarchy) //just enumerate once
{
if (!parents.ContainsKey(row.ParentGuid)
{
Parent newParent = new Parent(row);
parents[row.ParentGuid] = newParent;
}
Child newChild = new Child(row);
Parent theParent = parents[row.ParentGuid];
theParent.Children.Add(newChild);
}
List<Parent> result = parents.Values.ToList();
Or you could use GroupBy to get a similar result.
from row in myFlattenedHierarchy
group row by row.ParentGuid into g
select new Parent()
{
Guid = g.Key,
Name = g.First().ParentName,
Children =
(
from childRow in g
select new Child()
{
Guid = childrow.ChildGuid,
Name = childrow.ChildName
}
).ToList()
}
It's a toss-up which is more maintainable. Either way, don't re-enumerate myFlattenedHierarchy inside the loop/query.
I believe you can use GroupBy() (full disclosure: not compiled):
myFlattenedHierarchy.GroupBy(row => row.ParentGuid)
.Select(group => new Parent
{
Guid = group.Key.ParentGuid,
Name = group.Key.ParentName,
Children = myFlattenedHierarchy.Where(c => c.ParentGuid == group.Key.ParentGuid)
.Select(c => new Child{ Guid = c.ChildGuid, Name = c.ChildName })
.ToList()
});
You need to tackle with recursivity and infinite recursivity if you have a loop from within your flat collection. Linq can't be used for the complete problem but can help returning child of a specific node.
This should work, very similar to David B's second example but I couldn't get his to work without a bit of fixing (grouping by multiple column) so I've added it here for the record.
from row in myFlattenedHierarchy
group row by new { row.ParentGuid, row.ParentName } into g
select new Parent()
{
Guid = g.Key.ParentGuid,
Name = g.Key.ParentName,
Children =
(
from childRow in g
select new Child()
{
Guid = childRow.ChildGuid,
Name = childRow.ChildName
}
).ToList()
};
精彩评论