Counting Nested List Objects While Traversing - c#

I am working on a project in C# where I have an object with properties, one of which is called Children which is the same as the parent object. For example:
public class ObjectInformation
{
public string FullName {get; set;}
public string FriendlyName {get; set;}
public List<ObjectInformation> Children {get;}
}
And I already have a method which flattens a root object into a simple list:
public static IEnumerable<ObjectInformation> Flatten(List<ObjectInformation> objs)
{
var localCopy = Helpers.General.DeepCopy(objs);
var finalList = new List<ObjectInformation>();
foreach(var obj in localCopy)
{
if(obj.Children.Count > 0)
{
finalList.AddRange(Flatten(obj.Children));
obj.Children.Clear();
}
obj.Parent = null;
finalList.Add(obj);
}
return finalList;
}
I know the above method could probably be improved, but it works right now. However, what I'm trying to do now is traverse the nested list and output the data, while indenting lines depending on the nesting level.
So, as an example, say the root object has two objects, the first one which has one child, and the second which has a child, which also has a child. I would want the output to be something like this:
FullName of Root Object 1
FullName of Child 1 of Root Object 1
FullName of Root Object 2
FullName of Child 1 of Root Object 2
FullName of Child 1 of Child 1 of Root Object 2
To do the indenting, I need some kind of counter to determine how deep the level is nested. I keep running into an issue using a recursive method though because on each call, the variable is reset. I was thinking, maybe I need to use a static variable to track the nesting level. The issue I see with that though is that as it travels back up, the static variable will still have the value of the deepest level its reached.
I'm kind of at a lost of how to proceed with this, albeit I'm sure it is probably a simple solution I just can't picture at the moment; I generally don't use/need recursion so I don't have a lot of experience actually using it.
Any help/suggestions you an provide would be greatly apprecated.
Thank You

I would recommend a method that takes the object and prints each with a defined space. Recursive call would add x amount of spaces each time you would go in deeper within the objects.
public static void Print(this ObjectInformation parent, int spacing = 2)
{
Console.WriteLine($"{new string(' ', spacing)}{parent.FullName}");
foreach (ObjectInformation child in parent.Children)
{
child.Print(spacing + 2);
}
}

I tried this same as an exercise and got a working result.
public void PrintInfoList(IEnumerable<ObjectInformation> list)
{
var result = string.Join("\n", list.Select(item => GetPrintedFormat(item)));
Console.WriteLine(result);
}
public string GetPrintedFormat(ObjectInformation info)
{
string printedFormat = string.Empty;
printedFormat = $"Fullname of {info.FriendlyName} - {info.FullName}";
if (info.Children != null && info.Children.Any())
{
childCount++;
_formatter = $"\n{string.Empty.PadRight(childCount, '\t')}";
printedFormat += $"{_formatter}{string.Join(_formatter, info.Children.Select(child => GetPrintedFormat(child)))}";
}
else
if (childCount > 0) childCount--;
return printedFormat;
}
this is tested working solution. Let me know what you think of this.
But i like to vote #Jawad's simplest way too. (clap)

Related

C# Recursive Search an array of Objects.parent_id for value, then search those and so on till none left

Looking for a solution to find an object.id and get all the parent_id's in an array of objects, and then set object.missed = true.
Object.id, and Object parent_id. If the object doesn't have a parent_id, parent_id = id.
I know how to do it for one level of parent_id's. How can I go unlimited levels deep? Below is the code I have for searching the 1 level.
public class EPlan
{
public int id;
public int parent_id;
public bool is_repeatable;
public bool missed;
}
EPlan[] plans = Array.FindAll(eventsPlan, item => item.parent_id == event_id);
foreach (EPlan plan in plans)
{
plan.missed = true;
plan.is_repeatable = false;
}
I'm trying to search for event_id an int. So I search all of the object.id's for event_id. Once I find object.id == event_id. I need to set object.is_repeatable = false and object.missed = true.
Then I need to search all of the objects.parent_id for current object.id (event_id). Change all of those object to the same as above.
Then I need to check all of those object.id's against all of the object.parent_id's and do the same to those. Like a tree affect. 1 event was missed, and any of the events that are parented to that event need to be set as missed as well.
So far, all I can do is get 1 level deep, or code multiple foreach loops in. But it could be 10 or more levels deep. So that doesn't make sense.
Any help is appreciated. There has to be a better way that the multiple loops.
I too was confused by the question, save for the one line you said:
1 event was missed, and any of the events that are parented to that event need to be set as missed as well.
With that in mind, I suggest the following code will do what you're looking for. Each time you call the method, it will find all of the objects in the array that match the ID and set the event as Missed and Is_Repeatable appropriately.
It also keeps a running list of the Parent_ID's it found during this scan. Once the loop is finished it will call itself, using the list of parent id values instead of the passed in list of events ids it just used. That is the trick that makes the recursion work here.
To start the process off, you call the method with the single event ID you did for 1-level search.
findEvents(new List<string>{event_id}, eventsPlan);
private void findEvents(List<int> eventIDs, EPlan[] eventsPlan)
{
foreach (int eventID in eventIDs)
{
EPlan[] plans = Array.FindAll(eventsPlan, item => item.parent_id == eventID);
List<int> parentIDs = new List<int>();
foreach (EPlan plan in plans)
{
plan.missed = true;
plan.is_repeatable = false;
parentIDs.Add(plan.parent_id);
}
if (parentIDs.Count > 0)
findEvents(parentIDs, eventsPlan);
}
}
I also recommend that if you have the chance to reengineer this code to not use arrays, but a Generic Collection (like List<EPlan>) you can avoid the performance penalty this code has because it's building new arrays in memory each time you call the Array.FindAll method. Using the Generic Collection, or even using old-school foreach loop will work faster when processing a lot of data here.
Update 1:
To answer your question about how you might go about this using a Generic Collection instead:
private void findEventsAsList(List<int> eventIDs, List<EPlan> eventsPlans)
{
List<int> parentIDs = new List<int>();
foreach (EPlan plan in eventsPlans.Where(p => eventIDs.Contains(p.parent_id)))
{
plan.missed = true;
plan.is_repeatable = false;
parentIDs.Add(plan.parent_id);
}
findEventsAsList(parentIDs, eventsPlan);
}

What's the best practice for finding an object in a List based on one of object's properties?

I'm trying to implement AI's A star algorithm in C#. As all of you may know, there is some place in my code where I want to check if a Node object (which I've defined in my code) is in my List<Node> nodes or not. But since the Node object in the List<Node> nodes has different reference with the one I've created to see if exists in the list, the output I get is False. (You can see this in the code below) :
class Node
{
bool[,] state;
Node parent;
String action;
int path_cost=0;
public int Cost
{
get
{
return path_cost;
}
}
public Node (bool[,] map)
{
this.state = map;
}
public double Heuristic()
{
return 0;
}
}
static void Main(string[] args)
{
bool[,] a = new bool[,] { { false, false }, { true, true } };
bool[,] a1 = new bool[,] { { false, false }, { true, true } };
Node temp = new Node(a);
Node n = new Node(a);
Node n1 = new Node(a1);
List<Node> nodes = new List<Node>();
nodes.Add(n);
Console.WriteLine(nodes.Contains(n1));
Console.ReadLine();
}
I've read about it and I know I should use LINQ and it's WHERE function. As you can see in my code, I want to check for n1's existence based on Node's state property which is a 2-dimensional array of booleans. Is using WHERE function best practice we can have here? I mean isn't there any problems with using it in my case?
Instead of Where(), I'd use Any(), like this:
Console.WriteLine(nodes.Any(n => ... ));
The trick here is the .... You mentioned comparing based on a 2D state array, but not what that comparison should look like. Does every item need to match? That seems... expensive. Is there a faster comparison? Have you thought about overriding .Equals()/GetHashCode()?
Additionally, depending on the size and nature of your List, you might do better with either a Dictionary or HashSet, both of which can potentially do much better than Any().
Finally, pay attention to the comments on your question from Eric Lippert. He's worked on a the C# team at Microsoft, and has a blog where he's already implemented A*. That ought to be a great reference for you.

For Each statement with a constructor list?

I am writing a pathfinding script in Unity, and I have been trying to figure out IEnumerables, and I feel I am not wording my internet searches correct, because it is not at all what I want.
I have 3 scripts, 1 find nodes and it adds to the OpenNodes list which is it's own script with constructor. I have An IEnumerable and it is still saying it can't access, or it needs to be static if just public. So my codes as follows:
public class OpenNodes: IEnumerator, IEnumerable {
public static Vector3 node;
public static int g;
public static IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
public OpenNodes(Vector3 newNode, int newGscore)
{
node = newNode;
g = newGscore;
}
so what I am trying to do in a different "FinderScript" is along these lines:
foreach(Vector3 node in OpenNodes)
I have been trying my hardest to figure it out, but I have never worked in constructor lists like this. This is new to me, so any help, would be SO greatly appreciated.. Thanks
EDIT:
This for each needs to take node in as a vector3 as it is using .x,.y, and .z values for comparison and changes..
As you need to store a score per node you could simply use a Dictionary<Vector3, int> to store the values.
e.g.
var nodes = new Dictionary<Vector3, int>();
nodes.add(node, gScore);
And then either access them directly
nodes[node] = ...
Or loop through them
foreach (var node in nodes.Keys) {
...
}
Alternatively, perhaps you can create an object that maps the score and the node together (or simply use a Pair and then just use a list?
var nodes = new List<Vector3>();
var map = new Pair(node, gScore);
nodes.add(map);
Then loop through them as per usual:
foreach (var node in nodes) {
...
}
Not sure if i have understood you correctly, and i haven't run this code. HTH.
The problem I was facing was actually more simple that I realized. Below is the code of my constructor list:
public class OpenNodes {
public Vector3 node;
public int g;
public OpenNodes(Vector3 newNode, int newGscore)
{
node = newNode;
g = newGscore;
}
}
The code for the foreach is as follow:
foreach(OpenNodes list in OpenNode)
to populate the list looks like:
OpenNode.Add(new OpenNodes(nodeLocation, 0 ));
to remove an item looks like:
OpenNode.RemoveAll(OpenNodes => nodeLocation == OpenNodes.node && 0 == OpenNodes.g);
and to access my node it is simply:
list.node.x
list.node
list.node.z
ect.
This is iterating through each item in the list as list, and then you call upon each variable after that. I hope this code will help someone in the future!

C# How to find a Class' non created objects?

I have this class
class Node
{
public bool is_end;
public int prefix_count;
public Node[] child = new Node[26];
}
My problem is, the class only creates Nodes if it is necessary, but later in the program, i need to check all the created Nodes, though since i don't know which ones are created, i can't use a cycle for it.
Is there a way for me to know which Nodes are created, is there a code to check if this Node exists or not.
Try using a List<Node> instead of a fixed length array. Then you can use methods such as
var nodeList = new List<Node>();
nodeList.Add(someRandomNode);
if (nodeList.Contains(someRandomNode))
{
// your logic
}
You may also iterate over your Node collection.
foreach(var node in nodeList) { }
In this case, your child array is pre-created, but each individual node is not allocated. You can check for null to see if a Node has been created:
bool ChildCreated(Node parent, int childIndex)
{
return parent.child[childIndex] != null;
}
I have to find out which child nodes are created, for example a cycle from 0 to 25, and it has to give a message like node[2] and node[11] are created, others aren't.
Using this method, you could easily do:
var created = Enumerable.Range(0, node.child.Length).Where(i => ChildCreated(node, i)).ToList();
if (!created.Any())
{
Console.WriteLine("No children created.");
}
else
{
Console.WriteLine("Children {0} created, all others aren't.", string.Join(", ", created));
}
All you need to do is to check if Node[] element is null:
int nodeIndex = 0;
if(child[nodeIndex] == null)
{
// node doesn't exist
}
else
{
// node exists
}

Unable to get my basic tree structure working

Sometimes you get one of those days no matter how much you batter your head around a wall, even the simplest task alludes you (this is one of those days!).
So what I have is a list of categories
CategoryID, CategoryName, ParentID, Lineage
1 Root Category, NULL, /1/
2 Child Category, 1, /1/2/
3 Grandchild, 2, /1/2/3
4 Second Root, NULL, /4/
5 Second Child 2, /1/2/5/
I've created a class to hold this where it contains all the values above, plus
ICollection<Category> Children;
This should create the tree
Root Category
`-- Child category
| `-- Grandchild
`-- Second Child
Second Root
So I'm trying to add a new category to the tree given the Lineage and the element, I convert the lineage to a queue and throw it into this function.
public void AddToTree(ref Category parentCategory, Category newCategory, Queue<Guid>lineage)
{
Guid lastNode = lineage.Dequeue();
if(lastNode == newCategory.CategoryId)
{
parentCategory.Children.Add(newCategory);
return;
}
foreach (var category in parentCategory.Children)
{
if(category.CategoryId == lastNode)
{
this.AddToTree(ref category, newCategory, lineage);
}
}
}
Now two problems I'm getting
The self referencing isn't too worrying (its designed to be recursive) but since the category in the foreach loop is a locally instantiated variable I can't make it by reference and use it as a pointer.
I'm sure there has to be an easier way than this!
Any pointers would be greatly received.
This code seems to be what you are looking for, but without any self references and recursions - it goes through the tree along the given lineage and in the end of the lineage inserts the given category.
Several assumptions:
Tree is stored as a list of its roots
lineage is a string
void AddCategory(List<Category> roots, Category categoryToAdd, string lineage)
{
List<Guid> categoryIdList = lineage.Split('/').Select(id => new Guid(id)).ToList();
List<Category> currentNodes = roots;
Category parentNode = null;
foreach (Guid categoryId in categoryIdList)
{
parentNode = currentNodes.Where(category => category.CategoryId == categoryId).Single();
currentNodes = parentNode.Children;
}
parentNode.Children.Add(categoryToAdd);
}
You dont appear to need the "ref" at all. You are not modifying the object reference, just its state.
EDIT:
If you must use ref, then use a temporary variable, for example...
foreach (var temp in parentCategory.Children)
{
Category category = temp;
if (category.CategoryId == lastNode)
{
this.AddToTree(ref category, newCategory, lineage);
}
}
But even with this, the ref is about useless. AddToTree does not modify the reference value. It modifies the referenced objects state. Maybe you have more code involved that we need to see.
If your intent is to modify the child reference in the parent, you will have an issue with ICollection Children object. You cannot use "ref" on an element in the ICollection to in effect replace the reference. You would have to remove the child reference and add a new one.

Categories

Resources