Iterate throuh list to determine hierarchy level of elements - c#

There is a List of items which contain a field called "HierarchyLevel" (type of String) which determines the hierarchy of elements like this: Link to image.
The tree structure would look like this:
<ul>
<li>1</li>
<ul>
<li>1.01</li>
<ul>
<li>1.01.01</li>
<li>1.01.02</li>
</ul>
<li>1.02</li>
<ul>
<li>1.02.01</li>
</ul>
<li>1.03</li>
</ul>
<ul>
And so on.
My goal is to implement a class which would contain the information about parent and children of each element.
So far I have this class:
class TreeNode<DBItem>
{
public DBItem Value { get; private set; }
public List<TreeNode<DBItem>> Children = new List<TreeNode<DBItem>>();
public TreeNode<DBItem> Parent { get; private set; }
public string Level { get; private set; }
public TreeNode (DBItem item, string level)
{
this.Value = item;
this.Level = level;
}
public TreeNode<DBItem> this[int i]
{
get { return this.Children[i]; }
}
public TreeNode<DBItem> AddChild(DBItem item, string level)
{
TreeNode<DBItem> node = new TreeNode<DBItem>(item, level) { Parent = this };
this.Children.Add(node);
return node;
}
}
The problem is I don't quite understand how to iterate through the collection of items. I tried this:
TreeNode<DBItem> parent = new TreeNode<DBItem>(neededItems[0], "1");
foreach (var doc in neededItems)
{
string level = doc.GetStringValue("HierarchyLevel");
if (level.StartsWith("1.")&& level.Length < 5)
{
var child1 = parent.AddChild(doc, level);
foreach (var child in neededItems)
{
string level1 = child.GetStringValue("HierarchyLevel");
if (level1.StartsWith("1."+level))
{
child1.AddChild(child, level1);
}
}
}
}
But obviously it is a bad approach.
I would like to get some help and advices on how to iterate through the list correctly.

We can achieve this using:
a Dictionary of all items (to help look-up parent nodes)
a List of root nodes (in case there is more than 1 root)
a list of DBItem objects that is ordered by hierarchy depth (1 before 1.01)
Sample implementation:
class so43271922
{
public so43271922()
{
}
[DebuggerDisplay("HierarchyLevel = {HierarchyLevel}")]
public class DBItem
{
public string Name { get; private set; }
public string HierarchyLevel { get; private set; }
public DBItem(string name, string hierarchyLevel)
{
this.Name = name;
this.HierarchyLevel = hierarchyLevel;
}
}
// Dummy list of DB Item objects
public readonly DBItem[] listItems = new DBItem[] {
new DBItem("Element 1", "1"),
new DBItem("Element 1.01", "1.01"),
new DBItem("Element 1.01.01", "1.01.01"),
new DBItem("Element 1.01.02", "1.01.02"),
new DBItem("Element 1.02", "1.02"),
new DBItem("Element 1.02.01", "1.02.01"),
new DBItem("Element 1.03", "1.03")
};
[DebuggerDisplay("HierarchyLevel = {Value.HierarchyLevel}")]
public class Node
{
public static IReadOnlyDictionary<string,Node> AllNodes { get { return allNodes; } }
public static IReadOnlyCollection<Node> Roots { get { return roots; } }
/// <summary>
/// Stores references to all nodex, using HierarchyLevel as key
/// </summary>
private static Dictionary<string, Node> allNodes = new Dictionary<string, Node>();
/// <summary>
/// Stores references to root nodes
/// </summary>
private static List<Node> roots = new List<Node>();
public DBItem Value { get; private set; }
public Node Parent { get; private set; }
public List<Node> Children { get; private set; }
public int Level { get; private set; }
public Node(DBItem li)
{
this.Children = new List<Node>();
this.Value = li;
allNodes.Add(li.HierarchyLevel, this);
if (li.HierarchyLevel.Contains("."))
{
var parentHier = li.HierarchyLevel.Substring(0, li.HierarchyLevel.LastIndexOf("."));
this.Parent = allNodes[parentHier];
this.Parent.Children.Add(this);
this.Level = this.Parent.Level + 1;
}
else
{
roots.Add(this);
this.Parent = null;
this.Level = 0;
}
}
}
public void generateHierarchy()
{
// Sort all items by: hierarchy depth, then by hierarchy level value
var sortedItems = listItems
.OrderBy(i => i.HierarchyLevel.Count(c => c == '.')); // 1 before 1.01
foreach (var item in sortedItems)
{
new Node(item);
}
var hier = Node.Roots;
}
}

Related

How to find any item from this Hierarchical Parent-Child Structure in C#

how to find any item from the list as it is dynamic, it may be in the parent or child in any position of list and need to stop finding when the list of child count will be 0, below is the Model of the list and and example of Hierarchical Parent-Child Structure
example:-
Parent->child->child->child or parent->child->child->child->child->child->child
how to find any item from the list as it is dynamic, it may be in the parent or children in any position of list and need to stop finding when the list of child count will be 0, below is the Model of the list and and example of Hierarchical Parent-Child Structure
example:-
Parent->child->child->child->Child or parent->child->child->child->child->child->child->Child
public partial class AllCategoryNodesModel
{
[JsonProperty("key")]
public Guid Key { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("parent_Category")]
public Guid? ParentCategory { get; set; }
[JsonProperty("children")]
public AllCategoryNodesModel[] Children { get; set; }
[JsonProperty("bannerURL")]
public Uri BannerUrl { get; set; }
[JsonProperty("iconURL")]
public Uri IconUrl { get; set; }
}
public partial class AllCategoryNodesModel
{
public static AllCategoryNodesModel[] FromJson(string json) => JsonConvert.DeserializeObject<AllCategoryNodesModel[]>(json, TestYourShop.Models.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this AllCategoryNodesModel[] self) => JsonConvert.SerializeObject(self, TestYourShop.Models.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
you can do a recursive search, for example:
Child SearchChild(string title, Child c)
{
if (c.title == title)
{
return c;
}
return SearchAmongChildren(title, c.children);
}
Child SearchAmongChildren(string title, List<Child> children)
{
foreach (var c in children)
{
if (c.title == title)
{
return c;
}
if (c.children != null)
{
var _c = SearchAmongChildren(title, c.children);
if (_c != null)
return _c;
}
}
return null;
}
Then, initiate the search:
var theChildThatIWant = SearchAmongChildren("the title that I want", parent.children);
var theChildThatIWant = SearchChild("the title that I want", child);
if (theChildThatIWant == null)
{
throw new ChildNotFoundException("Sorry, the child that you're looking for is not available.");
}
You can use BFS or DFS search.
bool Exist(Parent parent, string key)
{
if (parent.key == key) return true;
if (parent.children == null) return false;
var queue = new Queue<Child>(parent.children);
while (queue.Any())
{
Child c = queue.Dequeue();
bool match = c.key == key;
if (match) return true;
if (c.children == null) continue;
foreach (Child grandChild in c.children)
{
queue.Enqueue(grandChild);
}
}
return false;
}
// TEST SCENERIO
Parent parent1 = new Parent();
var result1 = Exist(parent1, "key-1");
Console.WriteLine($"Actual : {result1}, Expected : False");
Parent parent2 = new Parent { children = new List<Child> { new Child() { key = "key-1" }, new Child() { key = "key-2" } } };
var result2 = Exist(parent2, "key-2");
Console.WriteLine($"Actual : {result2}, Expected : True");
Parent parent3 = new Parent { children = new List<Child> { new Child() { key = "key-1" }, new Child() { key = "key-2", children = new List<Child> { new Child() { key = "key-5" } } } } };
var result3 = Exist(parent3, "key-5");
Console.WriteLine($"Actual : {result3}, Expected : True");
var result4 = Exist(parent3, "key-21323");
Console.WriteLine($"Actual : {result4}, Expected : False");

Multiple loops to Recursive method c#

I have an object with multiple nested objects in it. The classes:
public class Element
{
public string Key { get; set; }
public List<Element> SubElement { get; set; }
[XmlIgnore]
public ElementConfig ElementConfig { get; set; }
[XmlIgnore]
public Element ParentElement { get; set; }
}
public class SubElement
{
public string Key { get; set; }
public List<Element> Elements { get; set; }
}
[Serializable]
public class ElementConfig {
public string Key { get; set; }
public string Label { get; set; }
public string TypeName { get; set; }
public string IconName { get; set; }
}
The base is that an Element can have multiple SubElements with their own ElementConfig. I want to populate the ElementConfig for each Element, doing it like this:
private void Recursion(Element element)
{
//TODO: use recursion ??
foreach (Element item in element.SubElement)
{
item.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == item.Key);
item.ParentElement = element;
foreach (Element x in item.SubElement)
{
x.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(e => e.Key == x.Key);
x.ParentElement = item;
foreach (Element y in x.SubElement)
{
y.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(e => e.Key == y.Key);
y.ParentElement = x;
}
}
}
}
This is working, but I want to use a Recursive method to do this. Is this possible?
EDIT
From the suggestion from #johnathan Barclay I use following approach:
element.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == element.Key);
foreach (var item in element.SubElement)
{
item.ParentElement = element;
Recursion(item);
}
This helps getting the ElementConfig for each first SubElement. What happens now is that the following SubElements are not filled.
Assuming ElementConfig also needs to be set for the root element:
private void Recursion(Element element)
{
element.ElementConfig = navigationStructureConfig.ElementConfigs
.SingleOrDefault(x => x.Key == element.Key);
foreach (var item in element.SubElement)
{
item.ParentElement = element;
Recursion(item); // Recurse here
}
}
Here is an generic iterative solution:
public void PupulateConfigAndParent(Element element){
foreach(var (item, parent) in DepthFirstWithParent(element, e => e.SubElement)){
item.ElementConfig = navigationStructureConfig.ElementConfigs.SingleOrDefault(x => x.Key == item.Key);
item.ParentElement = parent;
}
}
public static IEnumerable<(T Node, T Parent)> DepthFirstWithParent<T>(T self, Func<T, IEnumerable<T>> selector)
{
var stack = new Stack<(T Node, T Parent)>();
stack.Push((self, self));
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in selector(current.Node))
{
stack.Push((child, current.Node ));
}
}
}
An advantage of this is the ability to reuse the generic method for any kind of tree like structure. Note that the root node will have itself as the parent.

C# reverse navigation with generic types

My classes are similar to these:
public class Root<TChild>
{
int ID {get;}
...
List<TChild> Children {get;}
}
public class Child
{
int IDChild {get;}
...
}
I need to add the reverse navigation property in Child class to access from Child its Root, but I don't know how to declare the property of Root<TChild> type.
Which type should be TChild in Child class?
I tried this in .net fiddle and seems to work just fine
public class Root<TChild>
{
int ID {get;}
List<TChild> Children { get; set; }
}
public class Child
{
int IDChild {get;}
Root<Child> MyRoot { get; set; }
}
// wherever
var root = new Root<Child> { Children = new List<Child>() };
root.Children.Add(new Child { MyRoot = root });
This works fine and problem is that you have two classes for same entry better is use one class for every entry in tree.
public class TreeEntity
{
private int id = 0;
private TreeEntity parent = null;
private IList<TreeEntity> childs = new List<TreeEntity>();
public TreeEntity(int id)
{
this.id = id;
}
public void AddChild(TreeEntity child)
{
childs.Add(child);
child.parent = this;
}
}
or maybe
public class TreeEntity<T>
{
private int id = 0;
private TreeEntity<T> parent = null;
private IList<TreeEntity<T>> childs = new List<TreeEntity<T>>();
private T data = null;
public TreeEntity(int id, T data)
{
this.id = id;
this.data = data;
}
public void AddChild(TreeEntity<T> child)
{
childs.Add(child);
child.parent = this;
}
}
however in your case
public class Root<TChild>
{
int ID {get;}
...
List<TChild> Children {get;}
...
public void addChild(T child)
{
Children.add(child)
child.setParent(this)
}
}
public class Child
{
int IDChild {get;}
Root<Child> parent;
...
public void setParent (Root<Child>)
{
this.parent = parent;
}
}

Implementing tree structure on c#

I have an object myBook.
Can I implement a better structure for that kind of data?
public class myRow{
public int ID = 0;
public int number = 0;
public String param1 = null;
public decimal param2 = null;
public string parm3 = "";
public int param4 = null;
}
public class mySubChapter{
public int ID = 0;
public string title = "";
public List<myRow> rows;
internal bool sort(){...} //sort rows by ID
}
public class myChapter{
public int ID = 0;
public string title = "";
public List<mySubChapter> subChapters;
internal bool sort(){...} //sort subChapters by ID
}
public class myBook{
public int ID = 0;
public string title = ""
public List<myChapter> chapters;
internal bool sort(){...} //sort chapters by ID
}
If you really want to model your book structure in a tree, you could use a generic tree implementation like the one presented here. Then, you could form a tree using code like this
DTreeNode<string> root = new DTreeNode<string>();
DTreeNode<string> temp;
temp = root.Nodes.Add("Hello");
temp.Nodes.Add("olleH");
temp = root.Nodes.Add("World");
temp.Nodes.AddRange(new string[]
{ "dWorl", "ldWor", "rldWo", "orldW" } );
In my opinion, I'll merge subchapter and chapper class into one myChaper class and add new property is chapterLevel in it. Because I think subChapter is a chapter too with just difference level(children of chapter may be). Sorry for my English.
public class myRow{
public int ID = 0;
public int number = 0;
public String param1 = null;
public decimal param2 = null;
public string parm3 = "";
public int param4 = null;
}
public class myChapter{
public int ID = 0;
public string title = "";
public int chapterLevel = 0;
internal bool sort(){...} //sort chapters by ID and level
}
public class myBook{
public int ID = 0;
public string title = ""
public List<myChapter> chapters;
internal bool sort(){...} //sort chapters by ID
}
Another tree implementation:
public interface INode
{
int Id { get; set; }
INode Parent { get; }
ReadOnlyCollection<INode> Children { get; }
void SetParent(INode node);
void AddChild(INode node);
}
public class Node : INode
{
private INode _parent;
private IList<INode> _children;
public Node()
{
_children = new List<INode>();
}
public int Id { get; set; }
public INode Parent
{
get { return _parent; }
}
public ReadOnlyCollection<INode> Children
{
get
{
return new ReadOnlyCollection<INode>
(_children.OrderBy(c => c.Id).ToList());
}
}
public virtual void AddNode(INode node)
{
_children.Add(node);
node.SetParent(this);
}
public virtual void SetParent(INode node)
{
_parent = node;
}
}
The classes, Row, Chapter, Book can derive from the Node class, e.g.
public class Book : Node
{
public override void SetParent(INode node)
{
throw new InvalidOperationException();
}
public string Title { get; set; }
}

Finding the root nodes of all the of a tree from a nodes in any generic list

This is a entity and i want to list all the children node for a given node in a generic function
public static List<T> BuildTree<T>(List<T> list, T selectNode string keyPropName, string parentPropName, string levelPropName, int level = 0)
{
List<T> entity = new List<T>();
foreach (T item in list)
{
}
return entity;
}
example of the entity structure
protected long _coakey;
protected long _parentkey;
protected string _coacode;
protected string _coacodeclient;
protected string _coaname;
protected int _coalevel;
[DataMember]
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChnaged(); }
}
[DataMember]
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChnaged(); }
}
[DataMember]
public string coacode
{
get { return _coacode; }
set { _coacode = value; this.OnChnaged(); }
}
[DataMember]
public string coacodeclient
{
get { return _coacodeclient; }
set { _coacodeclient = value; this.OnChnaged(); }
}
[DataMember]
public string coaname
{
get { return _coaname; }
set { _coaname = value; this.OnChnaged(); }
}
[DataMember]
public int coalevel
{
get { return _coalevel; }
set { _coalevel = value; this.OnChnaged(); }
}
Your BuildTree<T> method cannot determine the structure of the tree unless it knows something about its structure. At a very minimum, I would suggest making a base class or interface that defines a tree node, and then change the BuildTree method to work specifically with those types of objects. Then, it will be able to figure out the tree structure. Each of you entity classes would have to implement that tree node interface or inherit from the tree node base class. For instance:
public abstract class TreeNodeBase
{
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChanged(); }
}
protected long _parentkey;
}
public class MyEntityTreeNode : TreeNodeBase
{
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChanged(); }
}
protected long _coakey;
// etc...
}
// Note the generic type constraint at the end of the next line
public static List<T> BuildTree<T>(List<T> list, T selectNode, string keyPropName, string parentPropName, string levelPropName, int level) where T : TreeNodeBase
{
List<T> entity = new List<T>();
foreach (TreeNodeBase node in list)
{
long parentKey = node.parentkey;
// etc...
}
return entity;
}
Node class:
public class Node<TKey, TValue> where TKey : IEquatable<TKey>
{
public TKey Key { get; set; }
public TKey ParentKey { get; set; }
public TValue Data { get; set; }
public readonly List<Node<TKey, TValue>> Children = new List<Node<TKey, TValue>>();
}
TreeBuilder:
public static Node<TKey, TValue> BuildTree<TKey, TValue>(IEnumerable<Node<TKey, TValue>> list,
Node<TKey, TValue> selectNode)
where TKey : IEquatable<TKey>
{
if (ReferenceEquals(selectNode, null))
{
return null;
}
var selectNodeKey = selectNode.Key;
foreach (var childNode in list.Where(x => x.ParentKey.Equals(selectNodeKey)))
{
selectNode.Children.Add(BuildTree(list, childNode));
}
return selectNode;
}
Usage:
List<MyClass> list = ...
var nodes = list.Select(x => new Node<long, MyClass>
{
Key = x.MyKey,
ParentKey = x.MyParentKey,
Data = x
}).ToList();
var startNode = nodes.FirstOrDefault(x => x.Data.Stuff == "Pick me!");
var tree = BuildTree(nodes, startNode);
MyClass example:
public class MyClass
{
public long MyKey;
public long MyParentKey;
public string Name;
public string Text;
public string Stuff;
}
I have solved it my self hope it help you
public static List<T> BuildTree<T>(List<T> list, T selectedNode, string keyPropName, string parentPropName, int endLevel = 0, int level = 0)
{
List<T> entity = new List<T>();
Type type = typeof(T);
PropertyInfo keyProp = type.GetProperty(keyPropName);
string _selectedNodekey = keyProp.GetValue(selectedNode, null).ToString();
PropertyInfo parentProp = type.GetProperty(parentPropName);
foreach (T item in list)
{
string _key = keyProp.GetValue(item, null).ToString();
string _parent = parentProp.GetValue(item, null).ToString();
if (_selectedNodekey == _parent)
{
T obj = (T)Activator.CreateInstance(typeof(T));
obj = item;
entity.Add(obj);
if (level == endLevel && level!=0) break;
entity.AddRange(BuildTree<T>(list, obj, keyPropName, parentPropName, level + 1));
}
}
return entity;
}

Categories

Resources