I have hierarchy tree, each element has a list of children and so on.
This is the class:
public class HierarchyItem
{
public int? HierarchyID { get; set; }
public string label { get; set; }
public List<HierarchyItem> children { get; set; }
public int Level { get; set; }
public int ParentID { get; set; }
}
I am trying to search for all the nodes that their label contains my search term.
I already managed to get the nodes that match my search term.
In addition i created a recursion to get all the parents (until the top node) for every node that match my search term.
The problem is that in this way i get a reverse tree:
Every children has parent that has parent and so on instead of getting parent with children that has a children and so on.
Any Idea? How to act?
I am not sure it will help, but this is the relevant code.
HierarchyItem result = new HierarchyItem();
var fullHierarchies = _entities.Hierarchies.Where(p => !p.Deleted).ToList();
var hierarchiesResult = _entities.Hierarchies.Where(p => !p.Deleted && p.Name.Contains(searchTerm)).ToList();
List<HierarchyItemWithParentAsHierarchyItem> itemsWithTrees = new List<HierarchyItemWithParentAsHierarchyItem>();
var hierarchyTop = new HierarchyItemWithParentAsHierarchyItem
{
HierarchyID = null,
label = "Organization",
Code = "0",
Path = string.Empty,
Level = 0,
Parent = null
};
foreach (var item in hierarchiesResult)
{
var hierarchy = new HierarchyItemWithParentAsHierarchyItem
{
HierarchyID = item.HierarchyID,
label = item.Name,
Code = item.Code,
Path = string.Empty,
Level = item.Level.Value,
};
hierarchy.Parent.Add(GetParent(fullHierarchies, item, hierarchyTop));
itemsWithTrees.Add(hierarchy);
}
private HierarchyItemWithParentAsHierarchyItem GetParent(List<CloudEntities.Hierarchy> fullHierarchies, CloudEntities.Hierarchy item, HierarchyItemWithParentAsHierarchyItem hierarchyTop)
{
try
{
HierarchyItemWithParentAsHierarchyItem tempHierarchyItem;
CloudEntities.Hierarchy tempHierarchy = fullHierarchies.FirstOrDefault(x => x.HierarchyID == item.ParentID);
if (tempHierarchy == null)
return hierarchyTop;
else
{
tempHierarchyItem = new HierarchyItemWithParentAsHierarchyItem()
{
HierarchyID = tempHierarchy.HierarchyID,
label = tempHierarchy.Name,
Code = tempHierarchy.Code,
Path = string.Empty,
Level = tempHierarchy.Level.Value
};
tempHierarchyItem.Parent.Add(GetParent(fullHierarchies, tempHierarchy, hierarchyTop));
}
return tempHierarchyItem;
}
catch (Exception ex)
{
}
}
Related
I need to build a hierarchy and I am using a dictionary.
I read strings randomly when I am trying to build this
and they have this format:
address.city.streetname.housenumber
address.areacode
address.city.streetname.coAddress
I have a problem figuring out how to populate the entire hierarchy
This is what I have done:
public class JsonElement
{
public string parent { get; set; }
public string name { get; set; }
public List<JsonElement> childrenJsonElements { get; set; }
}
var dictionaryHierarchy = new Dictionary<string, JsonElement>();
List<string> stringList = new List<string>()
{ "address.city.streetname.housenumber",
"address.areacode",
"address.city.streetname.coAddress"};
foreach(string element in stringList)
{
string[] tagsStringArray = element.Split('.');
if (!dictionaryHierarchy.ContainsKey(tagsStringArray[0]))
{
dictionaryHierarchy.Add(tagsStringArray[0], new JsonElement());
}
dictionaryHierarchy = AddElementsToHierarchy();
}
private static Dictionary<string, JsonElement> AddElementsToHierarchy(Dictionary<string,
JsonElement> dictionaryHierarchy, string element)
{
JsonElement jsonElement = new JsonElement();
string[] tagsStringArray = element.Split('.');
if (tagsStringArray.Length < 2)
{
return dictionaryJsonHierarchy;
}
jsonElement = dictionaryHierarchy[tagsStringArray[0]];
int ix = 1;
while (ix < tagsStringArray.Length)
{
if (jsonElement.name != tagsStringArray[ix])
{
jsonElement.parent = tagsStringArray[ix-1];
jsonElement.name = tagsStringArray[ix];
}
else
{
; // This part is for adding children
}
ix++;
}
return dictionaryHierarchy;
}
You have a tree structure made up of JsonElement nodes. This structure is the only data structure you need. Let's redefine JsonElement:
public class JsonElement
{
public string Parent { get; set; }
public string Name { get; set; }
public List<JsonElement> Children { get; } = new List<JsonElement>();
}
We made the properties PascalCase. This is the usual naming convetion. The Children are a read-only property with an initializer which instantiates the list.
As suggested, we add some more examples:
var input = new[] {
"address.city.streetname.housenumber",
"address.areacode",
"address.city.streetname.coAddress",
"person.name.firstname",
"person.name.lastname"
};
Now, we have two different elements at the start of the hierarchy. To enable this scenario, we add a neutral root element with a null name.
var root = new JsonElement();
foreach (string s in input) {
AddElements(root, s.Split('.'));
}
Now, let's create the hierarchy.
Adding elements consists of walking down the tree structure by following the tags (names). If one is missing, we add it.
private static void AddElements(JsonElement node, string[] elements)
{
foreach (string element in elements) {
var child = node.Children.Find(child => child.Name == element);
if (child == null) {
child = new JsonElement {
Parent = node.Name,
Name = element
};
node.Children.Add(child);
}
node = child; // Walk down the tree
}
}
We can test the result with this recursive method:
private static void PrintChildren(JsonElement node, int level = 0)
{
string indent = new String(' ', 4 * level);
foreach (var child in node.Children) {
Console.WriteLine($"{indent}{child.Name}, Parent = {child.Parent}");
PrintChildren(child, level + 1);
}
}
Called with PrintChildren(root); it prints:
address, Parent =
city, Parent = address
streetname, Parent = city
housenumber, Parent = streetname
coAddress, Parent = streetname
areacode, Parent = address
person, Parent =
name, Parent = person
firstname, Parent = name
lastname , Parent = name
See also:
Data Structure and Algorithms - Tree
Tree (data structure)
I am trying to create my own treeview binary tree where it shows folders and children / parents for certain folders.
I have a big list of folders then I have it in a custom object the Index, Parent Name, Children bool, and display name on the tree.
I can create the tree but when it creates it all the children are under the first node and not under the correct ones.
I see that treenode has level and parent if I could set those then this would be easy but I see they are readonly so I’m kinda stuck so far…
List<TreeNodeEnd> lstTreeNod = new List<TreeNodeEnd>();
Dictionary<int, TreeNode> valuePairs = new Dictionary<int, TreeNode>();
foreach (var node in lstTreeNod)
{
TreeNodeCollection items;
TreeNode treeNode;
if (node.TreeIndex == 0)
{
//node.DisplayName = "PDM Vault";
//TreeNode root2 = new TreeNode("PDM Vault", 3, 3, items);
//root.Name = "PDM Vault";
items = treeView1.Nodes;
//items = items.Add(node.DisplayName);
//treeView1.Nodes
}
else //in this ELSE DETERMINE ABOUT FATHER & CHILDREN EQUALS AND NOT NULL
{ // ValuePairs = Final Tree ?
//items = valuePairs[node.TreeIndex - 1].Nodes;
items = valuePairs[node.TreeIndex].Nodes;
TreeNode treeNode = items.Add(node.DisplayName);
}
TreeNode treeNode = items.Add(node.DisplayName);
if (valuePairs.ContainsKey(node.TreeIndex))
{
if (node.HasChildren == true)
valuePairs[node.TreeIndex] = treeNode;
}
else
{
valuePairs.Add(node.TreeIndex, treeNode);
}
}
public class TreeNodeEnd
{
public string DisplayName { get; set; }
public int TreeIndex { get; set; }
public bool HasChildren { get; set; }
public string Father { get; set; }
}
I have the following Model, which can be a child of the same type, and/or have many children of the same type.
public class SystemGroupModel
{
public int Id { get; set; }
public int? ParentSystemGroupModelId { get; set; }
[ForeignKey("ParentSystemGroupModelId")]
public virtual SystemGroupModel ParentSystemGroupModel { get; set; }
[InverseProperty("ParentSystemGroupModel")]
public virtual ICollection<SystemGroupModel> SubSystemGroupModels { get; set; }
}
How can I get a specific SystemGroupModel by Id that is at an unknown depth, and include all of it's children, grandchildren, great-grandchildren, etc.?
This appears to be working, which means it will build a list of any given parent, as well as children at an unlimited depth of grandchildren, great-grandchildren, and so on.
Is this the right approach?
First I created this new class
public class SystemGroupWorkingClass
{
public SystemGroupModel SystemGroupModel { get; set; }
public bool WasChecked { get; set; }
}
Then I added this code
EDIT: Updated to include the loop that builds List<SystemGroupWorkingClass>
List<SystemGroupWorkingClass> tempListSystemGroups = new List<SystemGroupWorkingClass>();
//Get the SystemGroups that were selected in the View via checkbox
foreach (var systemGroupVM in viewModel.SystemGroups)
{
if (systemGroupVM.Selected == true)
{
SystemGroupModel systemGroupModel = await db.SystemGroupModels.FindAsync(systemGroupVM.Id);
SystemGroupWorkingClass systemGroupWorkingClass = new SystemGroupWorkingClass();
systemGroupWorkingClass.SystemGroupModel = systemGroupModel;
systemGroupWorkingClass.WasChecked = false;
systemGroupWorkingClass.Selected = true;
//Make sure tempListSystemGroups does not already have this systemGroupWorkingClass object
var alreadyAddedCheck = tempListSystemGroups
.FirstOrDefault(s => s.SystemGroupModel.Id == systemGroupVM.Id);
if (alreadyAddedCheck == null)
{
tempListSystemGroups.Add(systemGroupWorkingClass);
}
}
}
for (int i = 0; i < tempListSystemGroups.Count; i++)
{
if (tempListSystemGroups[i].WasChecked == false)
{
SystemGroupModel systemGroupModel2 = await db.SystemGroupModels.FindAsync(tempListSystemGroups[i].SystemGroupModel.Id);
//Get the children, if there are any, for the current parent
var subSystemGroupModels = systemGroupModel2.SubSystemGroupModels
.ToList();
//Loop through the children and add to tempListSystemGroups
//The children are added to tempListSystemGroups as it is being iterated over
foreach (var subSystemGroupModel in subSystemGroupModels)
{
SystemGroupModel newSystemGroupModel = await db.SystemGroupModels.FindAsync(subSystemGroupModel.Id);
SystemGroupWorkingClass subSystemGroupWorkingClass = new SystemGroupWorkingClass();
subSystemGroupWorkingClass.SystemGroupModel = newSystemGroupModel;
subSystemGroupWorkingClass.WasChecked = false;
tempListSystemGroups.Add(subSystemGroupWorkingClass);
}
}
//Mark the parent as having been checked for children
tempListSystemGroups[i].WasChecked = true;
}
I have the following model
public class Node
{
public int AutoIncrementId { get; set; }
public string Text { get; set; }
public List<Node> Nodes { get; set; }
...//other propeties
}
I want to transform the data into the following model,
public class TreeView
{
public int Id {get; set;}
public string Text {get; set;}
public List<TreeView> Items {get; set;}
}
I started with the following, but then realised how am I going to know when to stop?
the variable test holds the node data
var items = test.Data.Select(x => new TreeViewItemModel
{
Id = x.AutoIncrementId.ToString(),
Text = x.Text,
Items = x.Nodes.Select(y=> new TreeViewItemModel(
{
Id = y.AutoIncrementId.ToString(),
Text = y.Text,
Items = //do I keep going?
}));
}
);
You can use recursion to do that:
public TreeView ConvertToTreeView(Node node)
{
TreeView tv = new TreeView();
tv.Id = node.AutoIncrementId;
tv.Text = node.Text;
if (node.Nodes != null && node.Nodes.Count > 0)
{
tv.Items = new List<TreeView>();
node.Nodes.ForEach(x => tv.Items.Add(ConvertToTreeView(x)));
}
return tv;
}
For clarity and simplicity, this works nicely:
public TreeView ConvertNode(Node rootNode)
{
var tree = new TreeView
{
Id = rootNode.AutoIncrementId,
Text = rootNode.Text,
Items = new List<TreeView>()
};
if (rootNode.Nodes != null)
{
foreach (var node in rootNode.Nodes)
{
tree.Items.Add(ConvertNode(node));
}
}
return tree;
}
I prefer this form.
public TreeView ConvertToTreeView(Node node)
{
return new TreeView
{
Id = node.AutoIncrementId;
Text = node.Text;
Items = node.Nodes.Select(ConvertToTreeView).ToList()
};
}
Edit: Yes Baldrick, I did :P and
public TreeView ConvertToTreeView(Node node)
{
return new TreeView
{
Id = node.AutoIncrementId;
Text = node.Text;
Items = node.Nodes != null
? node.Nodes.Select(ConvertToTreeView).ToList()
: new List<TreeView>()
};
}
Just doesn't look as nice.
I have a List of objects of type IGroup. These can be nested to an umlimited level, and I'm trying to group them after retrieving them from a database. I can't get my head around how to recursively add all groups to the right parents. Any groups with null as a parent are top level groups. I can't guarantee the order they come out of the database.
public interface IGroup {
string ID { get; set; }
string Name { get; set; }
string ParentID { get; set; }
IList<IGroup> Groups { get; set; }
...
So if I had a list of:
Group1: ID = g1, ParentID = null
Group1a: ID = g2, ParentID = g1
Group2: ID = g3, ParentID = null
Group1b: ID = g4, ParentID = g3
Group1bc: ID = g5, ParentID = g4
I'm trying to group them as:
|Group1
|--Group1a
|--Group1b
|--|
|--Group1bc
|Group2
Anyone fancy a stab at grouping them recursively?
No need to be recursive. To wit:
var lookup = items.ToDictionary(g => g.ID); // items is IEnumerable<IGroup>
foreach (var item in items.Where(g => g.ParentID != null)) {
lookup[item.ParentID].Groups.Add(item);
}
var parents = items.Where(g => g.ParentID == null);
Note that lookup[item.ParentID] will throw if there is no IGroup with the corresponding ParentID. You can handle this more gracefully with TryGetValue.
My implementation of IGroup:
public class Group : IGroup {
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
public IList<IGroup> Groups { get; set; }
public Group() {
Groups = new List<IGroup>();
}
}
My test items:
IEnumerable<IGroup> items = new List<IGroup>() {
new Group() { ID = "g1", ParentID = null },
new Group() { ID = "g2", ParentID = "g1" },
new Group() { ID = "g3", ParentID = null },
new Group() { ID = "g4", ParentID = "g3" },
new Group() { ID = "g5", ParentID = "g4" },
new Group() { ID = "g6", ParentID = "g5" }
};
This is not recursive, but here's a solution (assuming you have all you groups in a list called groups)
var rootGroups = new List<IGroup>();
var dic = groups.ToDictionary(g => g.ID);
foreach (var g in groups)
{
if (g.ParentID == null)
{
rootGroups.Add(g);
}
else
{
IGroup parent;
if (dic.TryGetValue(g.ParentID, out parent))
{
parent.Groups.Add(g);
}
}
}
You could try ordering them by parent id, assuming that the parent group is always created before the child group.
Group by ParentID (Linq: GroupBy), order by ID.
Start with an empty root node (ID: null) and add all items with this ParentID. Recursively continue this process for any item that has been added.
As you extract each element from the database, you need to add it to its parent. So keep a Dictionary to help find the parent. If you get a child before its parent, then you can insert a placeholder until you get the real thing.
void BuildGroups()
{
foreach( IGroup x /* obtained from database or a collection or wherever */ )
AddGroup( x );
}
Dictionary<string,IGroup> _groups = new Dictionary<string,IGroup>;
string _parentGroupName = "PARENT";
void AddGroup( IGroup x )
{
// locate (or create) parent and add incoming group
IGroup parent;
string parentID = x.ParentID ?? _parentGroupName;
if( !groups.TryGetValue( parentID, out parent ) )
{
parent = new Group( parentID ); // SEE NOTE BELOW!
_groups[parentID] = parent;
}
parent.Groups.Add( x );
// locate (or insert) child, and make sure it's up to date
IGroup child;
if( groups.TryGetValue( x.ID, out child ) )
{
// We must have inserted this ID before, because we found one
// of ITS children first. If there are any other values besides
// ParentID and ID, then copy them from X to child here.
}
else
{
// first time we've seen this child ID -- insert it
_groups[x.ID] = x;
}
}
The dictionary element at _parentGroupName will then be a dummy node whose children are all of your top-level groups (i.e. the ones with NULL as ParentID from the database); from that element you can do a recursive traversal:
VisitGroups( _groups[_parentGroupName], "" );
void VisitGroups( string ID, string indent )
{
IGroup x;
if( _groups.TryGetValue( ID, out x ) )
{
foreach( IGroup y in x.Groups )
{
WriteLine( indent + " {0}", y.ID );
VisitGroups( y.ID, indent + " " );
}
}
}
NOTE: This implementation runs in a single inline pass through the data -- you can add elements immediately as they're retrieved from the database, and you only need to make a single pass through the data. That means you save some time and some memory. But in return, it requires that you be able to allocate an object with type IGroup() to act as a placeholder, in case a child is retrieved before its parent. You can only avoid that requirement if you know something about the order of the objects or if you process your dictionary in two passes, as is shown in the other answers.
I use a sentinel value, _parentGroupName, to keep the top-level nodes in the same collection as all the others. You can easily alter this to use a separate collection for the top level nodes instead if you prefer.
You could try this
public interface IGroup
{
string ID { get; set; }
string Name { get; set; }
string ParentID { get; set; }
List<IGroup> Groups { get; set; }
}
public class Group : IGroup
{
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
public List<IGroup> Groups { get; set; }
public Group()
{
}
public Group(string id, string name, List<IGroup> childs)
{
ID = id;
Name = name;
Groups = (List<IGroup>)childs.Cast<IGroup>();
}
}
class Program
{
static void Main(string[] args)
{
List<IGroup> OriginalList;
List<IGroup> HirarchList = new List<IGroup>();
OriginalList = new List<IGroup>()
{
new Group() { ID = "g1", ParentID = null },
new Group() { ID = "g2", ParentID = "g1" },
new Group() { ID = "g3", ParentID = null },
new Group() { ID = "g4", ParentID = "g3" },
new Group() { ID = "g5", ParentID = "g4" },
new Group() { ID = "g6", ParentID = "g5" } };
HirarchList = GetCreateList(null, OriginalList);
}
public static List<IGroup> GetCreateList(string id, List<IGroup> list)
{
List<IGroup> temp = new List<IGroup>();
temp = (from item in list
where item.ParentID == id
select (IGroup)new Group(item.ID, item.Name,GetCreateList(item.ID, list))).ToList();
return (List<IGroup>)temp;
}
}