Traversing all descendants of an object - c#

I'm having problems with traversing all descendants of an object.
The 'unit' in the code below is of type Unit in my program. It has a property ChildUnits which returns a List<Unit> of the children of the unit.
I can successfully perform operations on the children. Then I check if those children have children, and if they do I can perform operations on them as well.
However, I need to check all descendants in case there is more depth than just grandchildren. I had a go with while loops in addition to the code below but it got really messy so I left it out.
This is the code I have reverted back to:
foreach (var child in unit.ChildUnits)
{
//do something here with the child (I know it sounds dodgy).
bool hasMoreChildren = child.ChildUnits.Count != 0;
if(hasMoreChildren)
{
foreach (var descendant in child.ChildUnits)
{
//do something here with the descendant.
}
}
}
I could just go another level deep as it's relatively rare for a unit to have more depth than that. But that's not a clean solution.
I think I might need to use a graph traversal algorithm and/or recursion perhaps, but I would like some advice on how to solve this problem most efficiently.
Edit: Is it possible to do this without defining a new function/method?

Edit: Is it possible to do this without defining a new function/method?
You could use an anonymous method...which is not exactly "not defining a new method", I know :)
However, there's another issue you should take care of: Circular references... even if you dont think there will be any
Here's an implementation, without defining any new method
Action<IEnumerable<Unit>> process = null;
var processed = new HashSet<Unit>();
process = list => {
foreach(var u in list.Where (processed.Add))
{
// do something here with u
//... and then process children
process(u.ChildUnits);
}
};
process(myList); // do the actual processing

Algorithm like this:
def traverse(Unit i):
for (Unit child : i.childList):
// Perform your logic for child
traverse(child)
This will perform the same function for each child for the first node , and when applying it for i.child[j] it will perform the same function for all i.child[j].child[k] so it will perform what you want for each node and all its childs.
instead you can use stack :
stack s;
s.push(firstNode);
while(!stack.empty()):
t = stack.pop()
foreach(Unit child : t):
s.push(child)
// Perform logic for child

You can use recursion:
void processChildren(List<Unit> children)
{
foreach (var child in children)
{
//do something here with the child (I know it sounds dodgy).
processChildren(child.Children); // recursive call here
}
}
If you don't want to define a new method, you could also roll your own stack:
var stack = new Stack<Unit>();
stack.push(firstUnit);
while( !stack.Any() ) {
var item = stack.pop();
//do something here with the item
foreach(var child in item.Children)
{
stack.push(child);
}
}

Another way to do this without recursion or an action/lambda, is to use a list with items to handle.
Like this.
var toDoList = new List<Unit> { unit };
while (toDoList.Any()) {
// Get current child, and remove it from the to-do-list
var currentChild = toDoList.First();
toDoList.RemoveAt(0);
// Do something with the current child.
// ...
// Now see, if the child has any children to handle
if (currentChild.ChildUnits != null && currentChild.ChildUnits.Any()) {
toDoList.AddRange(currentChild.ChildUnits);
}
}

Related

Make function dynamic / use Iteration / Make DRY

I want to make this function dynamic/Dry
rather than having to do multiple loops, do one loop /recursively
step1: The function receives a path
from that path it creates a Dir Model for the parent directory,
if the parent has sub directories, it adds them as models, to the parent subdirectory list,
step2: for each of the sub directories it repeats what it did in step 1, with the child being the parent to its sub directories if it has them,
this should repeat as long as a subdirectory to a parent has subdirectories,
finally, it returns the main Directory with its sub directories, as models with their own subdirectory models
public string RepoAsJson(string repoPath)
{
string path = #"C:\parentDir\subDir\subSubDir";
string[] parentDir = Directory.GetDirectories(path);
RepoDirModel model = new RepoDirModel();
foreach (string dir in parentDir){
RepoDirModel model1 = new RepoDirModel();
model1.Name = rket.Split(Path.DirectorySeparatorChar).Last();
model.SubDirectories.Add(model1);
foreach (string sub in Directory.GetDirectories(rket))
{
RepoDirModel model2 = new RepoDirModel();
model2.Name = sub.Split(Path.DirectorySeparatorChar).Last();
model1.SubDirectories.Add(model2);
foreach (string subSub in Directory.GetDirectories(sub))
{
RepoDirModel model3 = new RepoDirModel();
model3.Name = subSub.Split(Path.DirectorySeparatorChar).Last();
model2.SubDirectories.Add(model3);
}
}
}
return JsonSerializer.Serialize(model);
}
Remove nested loops / use Iteration / Make function dynamic
Since this seems to be some sort of course work / homework / training (maybe?), I am not going to post a working solution, but rather give you some hints.
First of all: What is the common "thing" your existing code does?
It gets the list of subdirectories from a specific path string and adds those into a tree structure.
That's it. So the first thing you might want to do is "extract" this functionality into a function or method, that only works on its parameters (no outside fields, "global vars" or something like that). You pass it the node to expand and the path to analyse and it will take care of one step.
Now, if you want to go into recursion, you need some additional things:
Do you want to go deep first or wide first?
When do you stop?
The second question is rather easy here: If there are no children (i.e. subdirectories) of the current subject, we are done - no further recursion.
The answer to the first one may not be so relevant in this setting, but think about it anyway. If for example you are looking for a specific leaf or node in the tree it might be relevant if it is more probable that you will find it quicker going deep first or go level by level.
But anyway: If you want to go deep first, you'd do something like this (Pseudo code)
RecursiveMethod( Element parent, ModelNode parentNode )
{
foreach (child of parent)
{
newNode = AddToModel(parentNode, child)
RecursiveMethod( child, newNode ) // <-- Note: Recursion IN the loop
}
}
And if you want to go wide something like
RecursiveMethod( List<Element> parents, Model m )
{
newParents = new List<Element>();
foreach( parent in parents )
{
foreach (child of parent)
{
AddToModel(m, parent, child)
newParents.Add(child);
}
}
// Note: Recursion AFTER the loop - in some cases it may even happen _before_
if( newParents.Length > 0 ) RecursiveMethod( newParents, m );
}
If you want an iterative solution, you have again those two questions, you just do a little different:
// you need some sort of datastructure, for "width first" we can use a FIFO queue
queue = new Queue(); // Not _the_ .NET Queue, just a FIFO for demo
// we start with the root...
queue.Enqueue( path );
// Then you do that one step as long as there is something in the queue
while( queue.Length > 0 )
{
current = queue.Dequeue(); // Get the first element in queue
foreach( child of current ) // No child -> no new elements in queue, length decreases
{
// If there are children =>
// Do what you need to do
InsertIntoModel(child); // Whatever that means in particular
// and put them into the queue for later processing
// queue length increases
queue.Enqueue(child);
}
}
If you want to go deep first, you can chose another mode of insertion and/or a different data structure. (For example a Stack, that is LIFO, creating width-first behavior)

Is there a simpler way that to skip from node explored in tree? [C#]

I have a tree that can have the same node.
If it found the node explored, then I want to skip the node.
Of course, this is a simple topic but I am curious if there is a simpler way.
The code that comes to mind is something like this:
void Explore(Tree tree, HashSet<Tree> exploredTrees)
{
if (exploredTrees.Contains(tree))
continue;
foreach(var childTree in tree.ChildTree)
{
Explore(childTree);
exploredTrees.Add(childTree);
}
}
void static Program()
{
// it assumes there is data in the tree.
Tree tree = new Tree();
Explore(tree, new HashSet());
}
I've been using the code above so far but the second parameter getting on my nerves (new HashSet() for the above example).
As you know to achieve this purpose it must need a data structure to store the data explored.
However, I'm not satisfied because the data structure has to pass from the external. (ex: Explore(tree, new HashSet()))
Is there a way to achieve this purpose without the second parameter in C#?
I don't want to use static keyword because it has to remember to clear the data structure at external.
Thank you for reading.
The general approach is correct, but you could simply add a helper method that creates the hashSet for you: void Explore(Tree tree) => Explore(tree, new HashSet<Tree>())
Or you could use an iterative solution that lets you keep the HashSet as a local variable:
public static IEnumerable<T> DepthFirstLoopSafe<T>(T self, Func<T, IEnumerable<T>> selector, IEqualityComparer<T> equalityComparer = default)
{
var stack = new Stack<T>();
var visited = new HashSet<T>(equalityComparer ?? EqualityComparer<T>.Default);
stack.Push(self);
while (stack.Count > 0)
{
var current = stack.Pop();
visited.Add(current);
yield return current;
foreach (var child in selector(current))
{
if (!visited.Contains(child))
{
stack.Push(child);
}
}
}
}
Called like DepthFirstLoopSafe(tree, t => t.ChildTree). I like to use generics to describe the iteration of trees, since it allows reuse of code for all kinds of trees, regardless of type or how the tree is described.

Querying a chain of list of lists with LINQ

I am working with an XML standard called SDMX. It's fairly complicated but I'll make it as short as possible. I am receiving an object called CategoryScheme. This object can contain a number of Category, and each Category can contain more Category, and so on, the chain can be infinite. Every Category has an unique ID.
Usually each Category contains a lot of Categories. Together with this object I am receiving an Array, that contains the list of IDs that indicates where a specific Category is nested, and then I am receiving the ID of that category.
What I need to do is to create an object that maintains the hierarchy of the Category objects, but each Category must have only one child and that child has to be the one of the tree that leads to the specific Category.
So I had an idea, but in order to do this I should generate LINQ queries inside a cycle, and I have no clue how to do this. More information of what I wanted to try is commented inside the code
Let's go to the code:
public void RemoveCategory(ArtefactIdentity ArtIdentity, string CategoryID, string CategoryTree)
{
try
{
WSModel wsModel = new WSModel();
// Prepare Art Identity and Array
ArtIdentity.Version = ArtIdentity.Version.Replace("_", ".");
var CatTree = JArray.Parse(CategoryTree).Reverse();
// Get Category Scheme
ISdmxObjects SdmxObj = wsModel.GetCategoryScheme(ArtIdentity, false, false);
ICategorySchemeMutableObject CatSchemeObj = SdmxObj.CategorySchemes.FirstOrDefault().MutableInstance;
foreach (var Cat in CatTree)
{
// The cycle should work like this.
// At every iteration it must delete all the elements except the correct one
// and on the next iteration it must delete all the elements of the previously selected element
// At the end, I need to have the CatSchemeObj full of the all chains of categories.
// Iteration 1...
//CatSchemeObj.Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Iteration 2...
//CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Iteration 3...
//CatSchemeObj.Items.ToList().SingleOrDefault().Items.ToList().SingleOrDefault().Items.ToList().RemoveAll(x => x.Id != Cat.ToString());
// Etc...
}
}
catch (Exception ex)
{
throw ex;
}
}
Thank you for your help.
So, as i already said in my comment, building a recursive function should fix the issue. If you're new to it, you can find some basic information about recursion in C# here.
The method could look something like this:
private void DeleteRecursively(int currentRecursionLevel, string[] catTree, ICategorySchemeMutableObject catSchemeObj)
{
catSchemeObj.Items.ToList().RemoveAll(x => x.Id != catTree[currentRecursionLevel].ToString());
var leftoverObject = catSchemeObj.Items.ToList().SingleOrDefault();
if(leftoverObject != null) DeleteRecursively(++currentRecursionLevel, catTree, leftoverObject);
}
Afterwards you can call this method in your main method, instead of the loop:
DeleteRecursively(0, CatTree, CatSchemeObject);
But as i also said, keep in mind, that calling the method in the loop, seems senseless to me, because you already cleared the tree, besides the one leftover path, so calling the method with the same tree, but another category, will result in an empty tree (in CatSchemeObject).
CAUTION! Another thing to mention i noticed right now: Calling to list on your Items property and afterwards deleting entries, will NOT affect your source object, as ToList is generating a new object. It IS keeping the referenced original objects, but a deletion only affects the list. So you must write back the resulting list to your Items property, or find a way to directly delete in the Items object. (Assuming it's an IEnumerable and not a concrete collection type you should write it back).
Just try it out with this simple example, and you will see that the original list is not modified.
IEnumerable<int> test = new List<int>() { 1, 2, 3, 4 , 1 };
test.ToList().RemoveAll(a => a != 1);
Edited:
So here is another possible way of going after the discussion below.
Not sure what do you really need so just try it out.
int counter = 0;
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
while(true)
{
var temp = list.Where(x => CatTree[counter++] == x.Id); // or != ? play with it .
list = temp.Items.ToList().SingleOrDefault();
if(list.Equals(default(list))
{
break;
}
}
}
I just translated you problem to 2 solutions, but I am not sure if you won't lose data because of the SingleOrDefault call. It means 'Grab the first item regardless of everything'. I know you said you have only 1 Item that is ok, but still... :)
Let me know in comment if this worked for you or not.
//solution 1
// inside of this loop check each child list if empty or not
foreach (var Cat in CatTree)
{
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
while(true)
{
list.RemoveAll(x => x.Id != Cat.ToString());
list = list.ToList().SingleOrDefault();
if(list.Equals(default(list))
{
break;
}
}
}
}
//solution 2
foreach (var Cat in CatTree)
{
var list = CatSchemeObj.Items.ToList();
//check before you call it or you will get an error
if(!list.Equals(default(list)))
{
CleanTheCat(cat, list);
}
}
//use this recursive function outside of loop because it will cat itself
void CleanTheCat(string cat, List<typeof(ICategorySchemeMutableObject.Items) /*Place here whatever type you have*/> CatSchemeObj)
{
CatSchemeObj.RemoveAll(x => x.Id != cat);
var catObj = CatSchemeObj.Items.ToList().SingleOrDefault();
if (!catObj.Equals(default(catObj)){
CleanTheCat(cat, catObj);
}
}
Thank you to whoever tried to help but I solved it by myself in a much easier way.
I just sent the full CategoryScheme object to the method that converted it in the XML format, then just one line did the trick:
XmlDocument.Descendants("Category").Where(x => !CatList.Contains(x.Attribute("id").Value)).RemoveIfExists();

Delete item in nested collections of Nth level

I'm having trouble trying to delete a item inside a tree structured object.
My object is as below
TreeNode
{
string name;
ObservableCollection<TreeNode> Children;
}
I thought if I recursively process through the tree and find my node and delete it but I ran into trouble.
I did something along the lines of
Updated:
DeleteNode(ObservableCollection<TreeNode> children, TreeNode nodetodelete)
{
if(children.remove(nodetodelete))
{
return;
}
else
{
foreach(var child in children)
{
DeleteNode(child, nodetodelete);
}
}
}
I realize while I was writing the code that I would eventually run into manipulation exception while iterating through a collection that has a chance of being changed.
I could build a giant change of for loops since I know exactly the max deep length(which I did for a place holder) but that seems really bad. . . .
Can anyone point me in a better general direction. I kind of wonder if my data structure is the cause of this.
Update:
This will look awful and kinda of code smell but I got the recursion to "work"
by throw a exception when I find my node.
DeleteNode(children, nodetodelete)
{
if(children.remove(nodetodelete)
{
throw FoundException();
}
else
{
foreach(var child in children)
{
DeleteNode(child, nodetodelete)
}
}
}
Is there any other way of breaking out of a recursion.
I would deal with this by making a small change to my design (assuming the snippet in your question is pseudocode for a class):
TreeNode
{
string name;
TreeNode Parent;
ObservableCollection<TreeNode> Children;
public void Delete()
{
Parent.Children.Remove(this);
}
}
This makes a little bit more work for you maintaining an extra reference when manipulating your object graph, but saves you a lot of effort and code when doing things like deletes as you can see above.
You haven't shown how you're constructing TreeNodes, but I'd make the parent and a collection for the children arguments of the constructor.
You can safely iterate over the collection of children nodes and remove them, as long as you don't change the original collection. This can be done by creating an array of the collection and iterating over that instead.
DeleteNode(ObservableCollection<TreeNode> children, TreeNode nodetodelete)
{
if (children.remove(nodetodelete))
{
return;
}
else
{
foreach (var child in children.ToArray())
{
// If anything is deleted in the collection, it will not break the iteration here, as we are iterating over an Array and not "children"
DeleteNode(child, nodetodelete);
}
}
}
This will create a new collection for you to iterate over. If a child node is deleted from children, the foreach loop will not throw an exception. That is because the original collection was changed, while we iterate over a secondary collection.

Is there an easier way to typecast with unknown types?

I am writing a function to recurse my XAML and add all the controls to a hashtable, with their names being the keys. Unfortunately it seems like I have to go through and list every possible type:
void Recurse_Controls(object start)
{
string start_type = start.GetType().ToString();
if (start_type == "StackPanel")
{
ControlsByName.Add(((StackPanel)start).Name, start);
foreach (object item in ((StackPanel)start).Children)
{
Recurse_Controls(item);
}
}
if (start_type == "Grid")
{
ControlsByName.Add(((Grid)start).Name, start);
foreach (object item in ((Grid)start).Children)
{
Recurse_Controls(item);
}
}
}
Is there a simpler way of doing this?
What you really want to do is get all the objects in the logical tree. The logical tree will include other things than just controls (RowDefinitions for instance), so you should check to ensure the name actually exists on the object before adding it to the dictionary.
Since the Name property is defined on FrameworkElement (and FrameworkContentElement, but they use AddOwner so that these two properties are actually the same instance), you can just use GetValue to retrieve the value. This should do what you need:
void Recurse_Controls(DependencyObject start)
{
if (start == null) return;
var name = (string)start.GetValue(FrameworkElement.NameProperty);
if (!String.IsNullOrEmpty(name))
ControlsByName.Add(name, start);
foreach (var child in LogicalTreeHelper.GetChildren(start))
Recurse_Controls(child);
}
Side note: this is easily made iterative by using a Queue instead of recursion:
void Add_Controls(DependencyObject start)
{
if (start == null) return;
var items = new Queue<DependencyObject>();
items.Enqueue(start);
while (items.Count > 0)
{
var item = items.Dequeue();
var name = (string)item.GetValue(FrameworkElement.NameProperty);
if (!String.IsNullOrEmpty(name))
ControlsByName.Add(name, item);
foreach (var child in LogicalTreeHelper.GetChildren(item))
items.Enqueue(child);
}
}
You could loop through the base types, like Control for buttons etc. or ContainerControl to loop through the nested types. That would cut the amount of if clauses a great deal.
I wonder why you would need something like this though. Wat exactly are you trying to do that you need to list all controls?
Edit:
Just realized you can't just use the base type like that; you need to replace start.GetType().ToString() with start.GetType().BaseType.ToString();
It won't get much better than you did it. You should however use the is clause instead of comparing strings. This is much safer and much faster. Also you might be able to use baseclasses in the check. For example if (start is ButtonBase) will be true for all derived classes too (if that is suitable for your needs).

Categories

Resources