C# algorithm for generating hierarchy - c#

I've got a text file that looks like this:
{ Id = 1, ParentId = 0, Position = 0, Title = "root" }
{ Id = 2, ParentId = 1, Position = 0, Title = "child 1" }
{ Id = 3, ParentId = 1, Position = 1, Title = "child 2" }
{ Id = 4, ParentId = 1, Position = 2, Title = "child 3" }
{ Id = 5, ParentId = 4, Position = 0, Title = "grandchild 1" }
I'm looking for a generic C# algorithm that will create an object hierarchy from this. A "Hierarchize" function, if you will, that turns this data into an object hierarchy.
Any ideas?
edit I've already parsed the file into .NET objects:
class Node
{
public int Id { get; }
public int ParentId { get; }
public int Position { get; }
public string Title { get; }
}
Now I need to actually arrange the objects into an object graph.

Many thanks to Jon and to mquander - you guys gave me enough information to help me solve this in a proper, generic way. Here's my solution, a single generic extension method that converts objects into hierarchy form:
public static IEnumerable<Node<T>> Hierarchize<T, TKey, TOrderKey>(
this IEnumerable<T> elements,
TKey topMostKey,
Func<T, TKey> keySelector,
Func<T, TKey> parentKeySelector,
Func<T, TOrderKey> orderingKeySelector)
{
var families = elements.ToLookup(parentKeySelector);
var childrenFetcher = default(Func<TKey, IEnumerable<Node<T>>>);
childrenFetcher = parentId => families[parentId]
.OrderBy(orderingKeySelector)
.Select(x => new Node<T>(x, childrenFetcher(keySelector(x))));
return childrenFetcher(topMostKey);
}
Utilizes this small node class:
public class Node<T>
{
public T Value { get; private set; }
public IList<Node<T>> Children { get; private set; }
public Node(T value, IEnumerable<Node<T>> children)
{
this.Value = value;
this.Children = new List<Node<T>>(children);
}
}
It's generic enough to work for a variety of problems, including my text file issue. Nifty!
****UPDATE****: Here's how you'd use it:
// Given some example data:
var items = new[]
{
new Foo()
{
Id = 1,
ParentId = -1, // Indicates no parent
Position = 0
},
new Foo()
{
Id = 2,
ParentId = 1,
Position = 0
},
new Foo()
{
Id = 3,
ParentId = 1,
Position = 1
}
};
// Turn it into a hierarchy!
// We'll get back a list of Node<T> containing the root nodes.
// Each node will have a list of child nodes.
var hierarchy = items.Hierarchize(
-1, // The "root level" key. We're using -1 to indicate root level.
f => f.Id, // The ID property on your object
f => f.ParentId, // The property on your object that points to its parent
f => f.Position, // The property on your object that specifies the order within its parent
);

Hmm... I don't quite see how that works. How can 2 and 5 both have parent=1, position=0? Should 5 have parent 2, 3 or 4?
Okay, this new version goes through the all the nodes three times:
Load all nodes and put them into the a map
Associate each node with its parent
Sort each node's child by position
It's not well-encapsulated, nicely error checking etc - but it works.
using System;
using System.Collections.Generic;
using System.IO;
public class Node
{
private static readonly char[] Braces = "{}".ToCharArray();
private static readonly char[] StringTrim = "\" ".ToCharArray();
public Node Parent { get; set; }
public int ParentId { get; private set; }
public int Id { get; private set; }
public string Title { get; private set; }
public int Position { get; private set; }
private readonly List<Node> children = new List<Node>();
public List<Node> Children { get { return children; } }
public static Node FromLine(string line)
{
Node node = new Node();
line = line.Trim(Braces);
string[] bits = line.Split(',');
foreach (string bit in bits)
{
string[] keyValue = bit.Split('=');
string key = keyValue[0].Trim();
string value = keyValue[1].Trim();
switch (key)
{
case "Id":
node.Id = int.Parse(value);
break;
case "ParentId":
node.ParentId = int.Parse(value);
break;
case "Position":
node.Position = int.Parse(value);
break;
case "Title":
node.Title = value.Trim(StringTrim);
break;
default:
throw new ArgumentException("Bad line: " + line);
}
}
return node;
}
public void Dump()
{
int depth = 0;
Node node = this;
while (node.Parent != null)
{
depth++;
node = node.Parent;
}
Console.WriteLine(new string(' ', depth * 2) + Title);
foreach (Node child in Children)
{
child.Dump();
}
}
}
class Test
{
static void Main(string[] args)
{
var dictionary = new Dictionary<int, Node>();
using (TextReader reader = File.OpenText("test.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
Node node = Node.FromLine(line);
dictionary[node.Id] = node;
}
}
foreach (Node node in dictionary.Values)
{
if (node.ParentId != 0)
{
node.Parent = dictionary[node.ParentId];
node.Parent.Children.Add(node);
}
}
foreach (Node node in dictionary.Values)
{
node.Children.Sort((n1, n2) =>
n1.Position.CompareTo(n2.Position));
}
Node root = dictionary[1];
root.Dump();
}
}
Sample text file:
{ Id = 5, ParentId = 4, Position = 0, Title = "grandchild 1" }
{ Id = 2, ParentId = 1, Position = 0, Title = "child 1" }
{ Id = 4, ParentId = 1, Position = 2, Title = "child 3" }
{ Id = 3, ParentId = 1, Position = 1, Title = "child 2" }
{ Id = 1, ParentId = 0, Position = 0, Title = "root" }
Output:
root
child 1
child 2
child 3
grandchild 1

Once you have the file parsed in you can follow this blog on how to assemble the objects into a hierarchy using LINQ.

I assume that your example incorrectly gives the wrong parent ID to object #5. This should cover it. Caveats: Assumes the "topmost" node always has a parent ID of zero. Ignores any nodes that aren't eventually descended from the topmost node. Behavior will be odd if presented with duplicate IDs.
public class FlatObj
{
public int Id;
public int ParentId;
public int Position;
public string Title;
}
public class Node
{
public int ID;
public string Title;
public IList<Node> Children;
public Node(FlatObject baseObject, IList<Node> children)
{
this.ID = baseObject.Id;
this.Title = baseObject.Title;
this.Children = children;
}
}
public static Node CreateHierarchy(IEnumerable<FlatObject> objects)
{
var families = objects.ToLookup(x => x.ParentId);
var topmost = families[0].Single();
Func<int, IList<Node>> Children = null;
Children = (parentID) => families[parentID]
.OrderBy(x => x.Position)
.Select(x => new Node(x, Children(x.Id))).ToList();
return new Node(topmost, Children(topmost.Id));
}
public static void Test()
{
List<FlatObj> objects = new List<FlatObj> {
new FlatObj { Id = 1, ParentId = 0, Position = 0, Title = "root" },
new FlatObj { Id = 2, ParentId = 1, Position = 0, Title = "child 1" },
new FlatObj { Id = 3, ParentId = 1, Position = 1, Title = "child 2" },
new FlatObj { Id = 4, ParentId = 1, Position = 2, Title = "child 3" },
new FlatObj { Id = 5, ParentId = 2, Position = 0, Title = "grandchild" }};
var nodes = CreateHierarchy(objects);
}

class Node {
public int Id { get;set; }
public int ParentId { get;set; }
public int Position { get;set; }
public string Title { get;set; }
public IEnumerable<Node> Children { get; set; }
public override string ToString() { return ToString(0); }
public string ToString(int depth) {
return "\n" + new string(' ', depth * 2) + Title + (
Children.Count() == 0 ? "" :
string.Join("", Children
.Select(node => node.ToString(depth + 1))
.ToArray()
);
}
}
class Program {
static void Main(string[] args) {
var data = new[] {
new Node{ Id = 1, ParentId = 0, Position = 0, Title = "root" },
new Node{ Id = 2, ParentId = 1, Position = 0, Title = "child 1" },
new Node{ Id = 3, ParentId = 1, Position = 1, Title = "child 2" },
new Node{ Id = 4, ParentId = 1, Position = 2, Title = "child 3" },
new Node{ Id = 5, ParentId = 3, Position = 0, Title = "grandchild 1" }
};
Func<Node, Node> transform = null;
transform = node => new Node {
Title = node.Title,
Id = node.Id,
ParentId = node.ParentId,
Position = node.Position,
Children = (
from child in data
where child.ParentId == node.Id
orderby child.Position
select transform(child))
};
Console.WriteLine(transform(data[0]));
}
}
result:
root
child 1
child 2
grandchild 1
child 3

Are you certain the last line's ParentID is 1? The title says grandchild, but it would be a child of "root" if i'm reading things correctly.

Here is the example that #baran asked for:
var lHierarchicalMenuItems = lMenuItemsFromDB.Hierarchize(0, aItem => aItem.Id, aItem => aItem.ParentId, aItem => aItem.Position);

Related

Creating a recursive list of objects C#

Hi I have a class like this:
public class Node
{
public Node();
public long NodeId { get; set; }
public int? LevelId { get; set; }
public ICollection<Node> Children { get; set; }
}
I have managed to recursively add Children to children. Now, printing the data is the problem.
Referencing from this: Recursive List Flattening
this returns a flattened list.
private IEnumerable<Node> GetNodes()
{
// Create a 3-level deep hierarchy of nodes
Node[] nodes = new Node[]
{
new Node
{
NodeId = 1,
LevelId = 1,
Children = new Node[]
{
new Node { NodeId = 2, LevelId = 2, Children = new Node[] {} },
new Node
{
NodeId = 3,
LevelId = 2,
Children = new Node[]
{
new Node { NodeId = 4, LevelId = 3, Children = new Node[] {} },
new Node { NodeId = 5, LevelId = 3, Children = new Node[] {} }
}
}
}
},
new Node { NodeId = 6, LevelId = 1, Children = new Node[] {} }
};
return nodes;
}
However, need data in this format. example.
NodeId | LevelId | ChildNodeId | ChildLeveld | ChildNodeId | ChildLevelId
1 | 1 | 2 | 2
1 | 1 | 3 | 2 | 4 | 3
1 | 1 | 3 | 2 | 5 | 3
like:
currently I have a class with nodeid and levelid. how can I dynamically create other childnodes and return as a collection of objects.
public Class New Class
{
public int NodeId {get;set;}
public int LevelId {get;set;}
public Dictionary<string, object> {get;set;}
// create dynamic childId and levelId in the dictionary
}
You leaf node should be null and not an empty list so you can test for null instead of a count of zero. You also do not need to columns for each level. See code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Data;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Node root = new Node();
root.setRoot();
root.BuildTable();
root.PrintTable();
Console.ReadLine();
}
}
public class Node
{
DataTable dt = new DataTable();
int maxDepth = 0;
List<Node> nodes { get; set; }
public long NodeId { get; set; }
public int LevelId { get; set; }
public List<Node> Children { get; set; }
public void setRoot()
{
nodes = GetNodes().ToList();
}
public void GetMaxDepth()
{
maxDepth = GetMaxDepthRecursive(nodes, 0);
}
public int GetMaxDepthRecursive(List<Node> nodes, int depth)
{
int returnDepth = depth + 1;
foreach (Node child in nodes)
{
if (child.Children != null)
{
int newDepth = GetMaxDepthRecursive(child.Children, depth + 1);
if (newDepth > returnDepth) returnDepth = newDepth;
}
}
return returnDepth;
}
private IEnumerable<Node> GetNodes()
{
// Create a 3-level deep hierarchy of nodes
Node[] nodes = new Node[]
{
new Node
{
NodeId = 1,
LevelId = 1,
Children = new List<Node>()
{
new Node { NodeId = 2, LevelId = 2, Children = null },
new Node
{
NodeId = 3,
LevelId = 2,
Children = new List<Node>()
{
new Node { NodeId = 4, LevelId = 3, Children = null },
new Node { NodeId = 5, LevelId = 3, Children = null }
}
}
}
},
new Node { NodeId = 6, LevelId = 1, Children = null }
};
return nodes;
}
public void GetTableRowsRecursive(List<Node> nodes, List<KeyValuePair<int,long>> parents)
{
string columnName = "";
foreach (Node node in nodes)
{
if (node.Children == null)
{
DataRow newRow = dt.Rows.Add();
if (parents != null)
{
foreach (KeyValuePair<int, long> parent in parents)
{
columnName = string.Format("Level {0} Node ID", parent.Key.ToString());
newRow[columnName] = parent.Value;
}
}
columnName = string.Format("Level {0} Node ID", node.LevelId);
newRow[columnName] = node.NodeId;
}
else
{
List<KeyValuePair<int, long>> newParents = new List<KeyValuePair<int, long>>();
if(parents != null) newParents.AddRange(parents);
newParents.Add(new KeyValuePair<int,long>(node.LevelId, node.NodeId));
GetTableRowsRecursive(node.Children, newParents);
}
}
}
public void BuildTable()
{
GetMaxDepth();
dt = new DataTable();
for (int i = 1; i <= maxDepth; i++)
{
string columnName = string.Format("Level {0} Node ID", i.ToString());
dt.Columns.Add(columnName, typeof(int));
}
GetTableRowsRecursive(nodes, null);
}
public void PrintTable()
{
string line = string.Join(" ",dt.Columns.Cast<DataColumn>().Select(x => string.Format("{0,-15}",x.ColumnName)));
Console.WriteLine(line);
foreach (DataRow row in dt.AsEnumerable())
{
line = string.Join(" ", row.ItemArray.Select(x => string.Format("{0,-15}", x)));
Console.WriteLine(line);
}
}
}
}
Output
What you're looking for is a recursive traversal of a tree. You track breadcrumbs along the way and build a new path to add to the results whenever you encounter a leaf node (one with no children).
In this output, each row shows ID and Depth separated by a comma, with each node in the path to the leaf node separated by the pipe symbol:
1,1 | 2,2
1,1 | 3,2 | 4,3
1,1 | 3,2 | 5,3
6,1
Press Enter to Quit...
Here is the code that generated that output:
class Program
{
public static void Main()
{
IEnumerable<Node> nodes = GetNodes(); // your test data
List<List<NodeData>> results = new List<List<NodeData>>(); // will store results
foreach (Node n in nodes)
{
n.Traverse(null, results); // start at each root node
}
// output the path to each leaf node from results
// *you can obviously format this differently to your liking
foreach(List<NodeData> path in results)
{
Console.WriteLine(String.Join(" | ", path.Select(p => p.ToString()).ToArray()));
}
Console.WriteLine("Press Enter to Quit...");
Console.ReadLine();
}
private static IEnumerable<Node> GetNodes()
{
// Create a 3-level deep hierarchy of nodes
Node[] nodes = new Node[]
{
new Node
{
NodeId = 1,
LevelId = 1,
Children = new Node[]
{
new Node { NodeId = 2, LevelId = 2, Children = new Node[] {} },
new Node
{
NodeId = 3,
LevelId = 2,
Children = new Node[]
{
new Node { NodeId = 4, LevelId = 3, Children = new Node[] {} },
new Node { NodeId = 5, LevelId = 3, Children = new Node[] {} }
}
}
}
},
new Node
{
NodeId = 6,
LevelId = 1,
Children = new Node[] {}
}
};
return nodes;
}
}
public class Node
{
public Node() {}
public long NodeId { get; set; }
public int? LevelId { get; set; }
public ICollection<Node> Children { get; set; }
public override string ToString()
{
return NodeId.ToString();
}
public void Traverse(List<Node> crumbs, List<List<NodeData>> results)
{
if (crumbs == null) { crumbs = new List<Node>(); }
crumbs.Add(this);
if (Children.Count == 0)
{
List<NodeData> path = new List<NodeData>();
foreach(Node n in crumbs)
{
path.Add(new NodeData() { NodeId = n.NodeId, LevelId = n.LevelId.Value });
}
results.Add(path);
}
else
{
foreach (Node n in Children)
{
n.Traverse(crumbs, results);
}
}
crumbs.Remove(this);
}
}
public class NodeData
{
public long NodeId { get; set; }
public int LevelId { get; set; }
public override string ToString()
{
return NodeId + "," + LevelId;
}
}

C# linq hierarchy tree

Tag class consists of ID Name and List<Tagging> :
public class Tag
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tagging> Tagging { get; set; }
}
Tagging class :
public class Tagging
{
public int Id { get; set; }
[ForeignKey("ParentTag")]
public int ParentId { get; set; }
public Tag ParentTag { get; set; }
[ForeignKey("ChildTag")]
public int ChildId { get; set; }
public Tag ChildTag { get; set; }
}
Tagging class just express many to many relationship between tags, for hierarchical purpose.
For example given a list :
List<Tag> tags = new List<Tag>();
var parent = new Tag {Name = "Parent", Id = 1, Tagging = new List<Tagging>{ new Tagging{ ParentId = 1, ChildId = 2}}};
var child = new Tag {Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3 }}};
var grandChild = new Tag {Name = "GrandChild", Id = 3};
tags.Add(parent);
tags.Add(child);
tags.Add(grandChild);
I am trying to loop through all hierarchical objects connected to his parent. For example if you call a method getAllHiearchyObject(Tag parent)
Output should be something like this :
Name : "Parent", Id = 1;
Name : "Child", Id : 2;
Name : "GrandChild", Id :3
I need an actual implementation of getAllHiearchyObject(Tag parent)
How about this...
static IEnumerable<Tag> FlattenTag(Tag root)
{
yield return root;
if (root.Tagging != null)
foreach (var childTagging in root.Tagging)
if (childTagging.ChildTag != null)
foreach (var grandChildTag in FlattenTag(childTagging.ChildTag))
yield return grandChildTag;
}
Note that the second foreach above allows for the use of yield with recursion.
Usage...
foreach(var tag in FlattenTag(root))
...
Only one parent to one child.
For a simple case when you have only one parent-child relationship you can create methods like:
public static class EnumerableExtensions
{
#region Methods
public static IEnumerable<T> Unwind<T>(T first, Func<T, T> getNext)
where T : class
{
if (getNext == null)
throw new ArgumentNullException(nameof(getNext));
return Unwind(
first: first,
getNext: getNext,
isAfterLast: item =>
item == null);
}
public static IEnumerable<T> Unwind<T>(
T first,
Func<T, T> getNext,
Func<T, Boolean> isAfterLast)
{
if (getNext == null)
throw new ArgumentNullException(nameof(getNext));
if (isAfterLast == null)
throw new ArgumentNullException(nameof(isAfterLast));
var current = first;
while(!isAfterLast(current))
{
yield return current;
current = getNext(current);
}
}
#endregion
}
And use them in the following way (I have set ChildTag in Taggings, as it will be done by EF):
List<Tag> tags = new List<Tag>();
var grandChild = new Tag { Name = "GrandChild", Id = 3 };
var child = new Tag { Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3, ChildTag = grandChild } } };
var parent = new Tag { Name = "Parent", Id = 1, Tagging = new List<Tagging> { new Tagging { ParentId = 1, ChildId = 2, ChildTag = child } } };
tags.Add(parent);
tags.Add(child);
tags.Add(grandChild);
var fromParent = EnumerableExtensions
.Unwind(
parent,
item =>
item?.Tagging?.FirstOrDefault()?.ChildTag)
.ToArray();
Console.WriteLine("Parent to child:");
foreach (var item in fromParent)
{
Console.WriteLine(item);
}
Proper parent to many children
For a proper tree creation you will have to use:
public class UnwoundItem<T> : IEnumerable<UnwoundItem<T>>
{
private readonly T _item;
private readonly IEnumerable<UnwoundItem<T>> _unwoundItems;
public UnwoundItem(T item, IEnumerable<UnwoundItem<T>> unwoundSubItems)
{
this._item = item;
this._unwoundItems = unwoundSubItems ?? Enumerable.Empty<UnwoundItem<T>>();
}
public T Item
{
get
{
return this._item;
}
}
public IEnumerable<UnwoundItem<T>> UnwoundSubItems
{
get
{
return this._unwoundItems;
}
}
public IEnumerator<UnwoundItem<T>> GetEnumerator()
{
return this._unwoundItems.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
and
public static class EnumerableExtensions
{
#region Methods
public static UnwoundItem<T> UnwindMany<T>(
T first,
Func<T, IEnumerable<T>> getNext)
where T : class
{
if (getNext == null)
throw new ArgumentNullException(nameof(getNext));
return UnwindMany(
first: first,
getNext: getNext,
isAfterLast: collection =>
collection == null);
}
public static UnwoundItem<T> UnwindMany<T>(
T first,
Func<T, IEnumerable<T>> getNext,
Func<IEnumerable<T>, Boolean> isAfterLast)
{
if (getNext == null)
throw new ArgumentNullException(nameof(getNext));
if (isAfterLast == null)
throw new ArgumentNullException(nameof(isAfterLast));
var currentItems = getNext(first);
if (isAfterLast(currentItems))
return new UnwoundItem<T>(
item: first,
unwoundSubItems: Enumerable.Empty<UnwoundItem<T>>());
return new UnwoundItem<T>(
item: first,
unwoundSubItems: currentItems
.Select(item =>
UnwindMany(
item,
getNext,
isAfterLast)));
}
#endregion
}
It can be tested with:
private static void Print<T>(IEnumerable<UnwoundItem<T>> items, Func<T, String> toString, Int32 level)
{
var indent = new String(' ', level * 4);
foreach (var item in items)
{
Console.Write(indent);
Console.WriteLine(toString(item.Item));
Print(item.UnwoundSubItems, toString, level + 1);
}
}
...
var grandChild = new Tag { Name = "GrandChild", Id = 3 };
var grandChild2 = new Tag { Name = "GrandChild 2", Id = 33 };
var child = new Tag { Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3, ChildTag = grandChild } } };
var child2 = new Tag { Name = "Child 2", Id = 22, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 33, ChildTag = grandChild2 } } };
var parent = new Tag { Name = "Parent", Id = 1,
Tagging = new List<Tagging> {
new Tagging { ParentId = 1, ChildId = 2, ChildTag = child },
new Tagging { ParentId = 1, ChildId = 2, ChildTag = child2 } }
};
var fromParent = EnumerableExtensions
.UnwindMany(
parent,
item =>
item?.Tagging?.Select(tagging => tagging.ChildTag));
Console.WriteLine("Parent to child:");
Print(new[] { fromParent }, item => item.Name, 0);

Efficient ordering of hierarchy data with only parentId

Problem
What is the fastest method to order a flat unordered set of nodes such that the parent is always listed before it's children. My current solution uses a queue to iterate the tree in a breadth first manner. However I've been wondering if there was a better / more efficient method.
Notes:
I can't pre compute any values
Id and ParentId could also be Guids (even if ints I can't guarantee sequential ids)
Linq Pad Code
void Main()
{
var nodes = new Node[] {
new Node { Id = 7, ParentId = 3 },
new Node { Id = 4, ParentId = 2 },
new Node { Id = 1, ParentId = 0 },
new Node { Id = 2, ParentId = 1 },
new Node { Id = 3, ParentId = 1 },
new Node { Id = 5, ParentId = 2 },
new Node { Id = 6, ParentId = 3 },
new Node { Id = 8, ParentId = 0 },
};
SortHierarchy(nodes).Dump();
}
IEnumerable<Node> SortHierarchy(IEnumerable<Node> list)
{
// hashtable lookup that allows us to grab references to the parent containers, based on id
var lookup = new Dictionary<int, List<Node>>();
Queue<Node> nodeSet = new Queue<Node>();
List<Node> children;
foreach (Node item in list) {
if (item.ParentId == 0) { // has no parent
nodeSet.Enqueue(item); // This will add all root nodes in the forest
} else {
if (lookup.TryGetValue(item.ParentId, out children)) {
// add to the parent's child list
children.Add(item);
} else {
// no parent added yet
lookup.Add(item.ParentId, new List<Node> { item });
}
}
}
while (nodeSet.Any()) {
var node = nodeSet.Dequeue();
if (lookup.TryGetValue(node.Id, out children)) {
foreach (var child in children) {
nodeSet.Enqueue(child);
}
}
yield return node;
}
}
private class Node {
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
}
Research
I did find this however it wasn't quite what I was after (also the code doesn't work)
Building hierarchy objects from flat list of parent/child
This code gives the same dump() result, order the list with parentid first:
IEnumerable<Node> SortHierarchy2(IEnumerable<Node> list)
{
return list.OrderBy(l => l.ParentId);
}
This will work as long as your child ids are < their parents ids.

How can I construct a tree using this data structure

I have this tree structure, which a node may have multiple nodes.
public class Node
{
public Node()
{
ChildLocations = new HashSet<Node>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual int? ParentLocationId { get; set; }
public virtual ICollection<Node> ChildLocations { get; set; }
}
Now, I want to add a list of parent-child value into this structure. like:
{1,A} -> {2,B}
{1,A} -> {3,C}
{1,A} -> {4,D}
{3,C} -> {5,E}
{3,C} -> {6,F}
to construct a tree looks like this:
1A
/ | \
2B 3C 4D
/ \
5E 6F
finally, it returns the root reference.
I have come out this solution. But I have no confidence with recursive part. It this correct?
public class Tree
{
Node root;
public Node Root
{
get { return root; }
}
public void Add(int parentId, string parentName, int childId, string childName)
{
if (root == null)
{
root = new Node { Id = parentId, Name = parentName };
root.ChildLocations.Add(new Node { Id = childId, Name = childName });
}
else
{
Add(root, parentId, parentName, childId, childName);
}
}
private void Add(Node node, int parentId, string parentName, int childId, string childName)
{
if (node == null)
{
return;
}
if (node.Id == parentId)
{
node.ChildLocations.Add(new Node { Id = childId, Name = childName });
return;
}
foreach (var n in node.ChildLocations)
{
Add(n, parentId, parentName, childId, childName);
}
}
}
As per my comment to your question, this works to build the tree you require:
public Node BuildTree()
{
var _1A = new Node() { Id = 1, Name = "A", };
var _2B = new Node() { Id = 2, Name = "B", };
var _3C = new Node() { Id = 3, Name = "C", };
var _4D = new Node() { Id = 4, Name = "D", };
var _5E = new Node() { Id = 5, Name = "E", };
var _6F = new Node() { Id = 6, Name = "F", };
_1A.ChildLocations.Add(_2B);
_1A.ChildLocations.Add(_3C);
_1A.ChildLocations.Add(_4D);
_3C.ChildLocations.Add(_5E);
_3C.ChildLocations.Add(_6F);
return _1A;
}
But it's not very general purpose. Can you elaborate on your needs?

Tree structure to get parent node and its children

Please find the table structure below:
Folderid parentFolderid Guid
1 0 1234
2 1 5678
3 2 9012
4 3 87697
5 7 4443
The requirement is if I pass folderId the function has to give me all the guids.
For example:If I pass 1 to the function, I should get first four Guids(parent and its children).
I have a function which returns all the guids as follows:
public List<Guid> Folders(int folderId)
{
// To get the folderids based on parentfolderid
var a = entity.Where(x => x.parentfolderId == folderId).FirstOrDefault();
return a;
}
I am able to get only up to one level of ids.
Is there any way to get parent, its children, grandchildren till the leaf?
If you are able to get that table to a class, check this out:
public class Entity
{
public int ID { get; set; }
public int ParentID { get; set; }
public string Name { get; set; }
public static List<Entity> GetTree(int ID, List<Entity> ListToSearch, bool First = true)
{
List<Entity> FilteredEntities = new List<Entity>();
FilteredEntities.AddRange(ListToSearch.Where<Entity>(x => x.ParentID == ID).ToList<Entity>());
List<Entity> Temp = new List<Entity>();
foreach (Entity current in FilteredEntities)
{
Temp.AddRange(GetTree(current.ID, ListToSearch, false));
}
FilteredEntities.AddRange(Temp);
if (First)
{
FilteredEntities.Add(ListToSearch.Where<Entity>(x => x.ID == ID).Single<Entity>());
}
return FilteredEntities;
}
}
Usage:
List<Entity> filteredEntities = Entity.GetTree(1, entities);
List<string> onlyTheNames = filteredEntities.Select<Entity, string>(x => x.Name).ToList<string>();
Regards
If you use this node class, you can write the code like this.
public class Folder
{
public int Id { get; set; }
public int? ParentId { get; set; }
public Guid SomeGuid { get; set; }
}
and and example of what possible is:
var list = new List<Folder>
{
new Folder {Id = 0, ParentId = null, SomeGuid = new Guid("0000b25b-8538-4b78-818a-9094507e0000") },
new Folder {Id = 1, ParentId = 0, SomeGuid = new Guid("1000b25b-8538-4b78-818a-9094507e0001") },
new Folder {Id = 2, ParentId = 1, SomeGuid = new Guid("2000b25b-8538-4b78-818a-9094507e0002") },
new Folder {Id = 3, ParentId = 1, SomeGuid = new Guid("3000b25b-8538-4b78-818a-9094507e0003") },
new Folder {Id = 4, ParentId = 2, SomeGuid = new Guid("4000b25b-8538-4b78-818a-9094507e0004") },
new Folder {Id = 5, ParentId = 3, SomeGuid = new Guid("5000b25b-8538-4b78-818a-9094507e0005") },
new Folder {Id = 6, ParentId = 0, SomeGuid = new Guid("6000b25b-8538-4b78-818a-9094507e0006") },
new Folder {Id = 7, ParentId = 4, SomeGuid = new Guid("7000b25b-8538-4b78-818a-9094507e0007") },
new Folder {Id = 8, ParentId = 3, SomeGuid = new Guid("8000b25b-8538-4b78-818a-9094507e0008") },
};
var rootNode = Node<Folder>.CreateTree(list, n => n.Id, n => n.ParentId).Single();
var firstChild = rootNode.Children.First(); // Id 1
var descendentsOfFirstChild = firstChild.Descendants; // All descendants of node 1

Categories

Resources