Read a generic List<T> with parent/child hierarchy into a datatable preserving the parent/child hierarchy
I have transformed this XML tree with the names of the units: Dim = Dimension
Dim1
|---MG1
|---M1
|---M2
|---M3
|---MG31
|---MG32
Dim2
|---MG220
|---MG2222
...into a List of Units aka List where every Unit can have another List with unlimited hierarchy. Now I want to transform the List to a tabular format with the parent/child hierarchy.
That way should the datatable look like:
Dimension...Parent..Child
Dim1........Dim1....MG1
Dim1........MG1.....M1
Dim1........MG1.....M2
Dim1........Dim1....MG2
Dim1........MG1.....M3
Dim1........M3......MG31
Dim1........M3......MG32
Dim2........Dim2....MG220
Dim2........MG220...MG2222
public class Unit
{
public String Name { get; set; }
public Unit Dimension { get; set; }
public UnitParent { get; set; }
public List<Unit> Children { get; set; }
}
Question: How would you iterate the List and write all the data into a DataTable ? There must be a tricky algoryth I can not 开发者_如何学JAVAfind.
Recursion is the right approach here, but I'm going to suggest a LINQ approach - especially as this question was tagged with LINQ.
One of the nice things with LINQ is that extension methods can take away a lot of complexity and leave simple queries that express the business logic simply.
So here's what I use to flatten recursive structures:
public static IEnumerable<T> Flatten<T>(this Func<T, IEnumerable<T>> @this, T root)
{
var head = new [] { root, };
var tail =
from c in @this(root)
where !c.Equals(root)
from d in @this.Flatten(c)
select d;
return head.Concat(tail);
}
Note the recursive call to Flatten
and that this is an extension method defined on the function that returns the children from a given parent item.
Now we can define the Func<T, IEnumerable<T>>
like this:
Func<Unit, IEnumerable<Unit>> f = u => u.Children;
And then, assuming that all Dimension
, Parent
, and Children
properties are not null, we can use this query to produce the list of records to add to the table:
var records =
from r in dimensions
from d in f.Flatten(r)
select new
{
Dimension = d.Dimension.Name,
Parent = d.Parent.Name,
d.Name,
};
Now, if any of the properties are null
, here's the fix.
Redefine f
as:
Func<Unit, IEnumerable<Unit>> f = u => u.Children ?? new List<Unit>();
And add this extension method:
public static R ValueOrNull<T, R>(this T @this, Func<T, R> selector)
where R : class
{
return @this != null ? selector(@this) : null;
}
Now the query works like this:
var records =
from r in dimensions
from d in f.Flatten(r)
select new
{
Dimension = d.Dimension.ValueOrNull(x => x.Name),
Parent = d.Parent.ValueOrNull(x => x.Name),
d.Name,
};
Still very similar, but null
safe.
I hope this helps.
A simple recursive algorithm will do fine...
function AddSubTree(Unit unit) {
if (unit != unit.Dimension)
AddItemToDataTable(unit.Dimension.Name, unit.UnitParent.Name, unit.Name);
foreach (Unit childUnit in unit.Children) {
AddSubTree(childUnit);
}
}
you call AddSubTree
for every dimension object you have
精彩评论