I have a list of objects with property id and parent_id.
I want to build a tree to link up those children and parents.
1 parent may have several children and there is an object which will be the ancestor of all objects.
What's the fastest algorithm to implement that?
I use C# as programming language, but other languages are also okay.
Something like that should do the trick :
public List<Node> MakeTreeFromFlatList(IEnumerable<Node> flatList)
{
var dic = flatList.ToDictionary(n => n.Id, n => n);
var rootNodes = new List<Node>();
foreach(var node in flatList)
{
if (node.ParentId.HasValue)
{
Node parent = dic[node.ParentId.Value];
node.Parent = parent;
parent.Children.Add(node);
}
else
{
rootNodes.Add(node);
}
}
return rootNodes;
}
(assuming that ParentId is a Nullable<int>, and is null for root nodes)
You could use a dictionary:
var dict = new Dictionary<Id, Node>();
foreach (var item in items)
{
dict[item.Id] = new Node(item);
}
foreach (var item in items)
{
dict[item.ParentId].AddChild(dict[item.Id]);
}
I much prefer this kind of structure. By maintaining a single list (you may want to use a dictionary or similar for speed) of items and passing it into the GetChildItems function you have greater flexibilty and ease of sorting, adding, removing, saving to a db etc.
You only really need the GetChildItem function when you are rendering the list to a view and you want the tree structure for easy editing as you say. In this case you can have a view model with the full list and the item which is passed into each item view
public class Item
{
public string Id { get; set; }
public string ParentId { get; set; }
public IEnumerable<Item> GetChildItems(List<Item> allItems)
{
return allItems.Where(i => i.Id == this.ParentId);
}
}
public class Tree
{
public List<Item> Items { get; set; }
public IEnumerable<Item> RootItems(List<Item> allItems)
{
return allItems.Where(i => i.ParentId == null);
}
}
Note: the class structure above is designed to mimic the traditional complex object pattern. these days you would prob just have GetChildItems(List allItems, Item parentItem) in the view model
Related
How to change value of an object in foreach from IEnumerable<Model>.
Code:
public IEnumerable<Model> ListDaftarPjsp()
{
IEnumerable<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list) {
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I already create Model. Then I am looping result using foreach, then set it. But the Return for conditionstill not changing? how to set condition inside foreach then return it for result
IEnumerable<T> is a query, not a collection. While there is some sort of collection at the other end, the query itself is not the collection. The nature of the collection you are targeting will determine whether or not you can modify the contents.
The general rule of thumb is that you can't expect an IEnumerable<T> to return the same list of objects twice, or even expect that you will be able to enumerate across it more than once - it is perfectly valid (if unusual) for an IEnumerable<T> to enumerate once only and refuse to enumerate a second or third time.
In this case what you have is actually a database query of type IQueryable<Model> that is cast to IEnumerable<Model>. It's still an IQueryable<Model> which means that each time you enumerate across it you will get (probably) the same list of data but in completely new objects. Changing one of the objects won't change all of the objects for the same source record, nor change the contents of the underlying record itself.
If you are trying to modify the returned objects without changing the underlying records (seems to be the case) then you need to materialize the query into a collection in memory. There are a few ways to do this depending on what you're expecting to do with the returned data.
The simplest is to convert the query to an array using the .ToArray() extension method:
public Model[] ListDaftarPjsp()
{
var query = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
var list = query.ToArray();
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
Now the records are in an array in memory and enumeration of that array can be done multiple times returning the same exact objects instead of fetching new copies of the data from the database every time.
Here you are trying to create a list of Model using LINQ, then you are iterating the same for adding an additional property to each item. Then why don't you add the property at the time of creation of the list instead for an additional loop? Make things simple by try something like this:
from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar,
condition = GetCondition(x.Foo)
};
Where the GetCondition() can be defined as :
private string GetCondition(int foo)
{
if(item.foo == 1)
{
return "a";
}
else if(item.foo == 2)
{
return "b";
}
else
{
return "xx";
}
}
There is already for this topic but there is more efficient way to do this.
Just use List<> instead of Array[].
public List<Model> ListDaftarPjsp()
{
List<Model> list = from x in db.PJSPEvaluation
select new Model
{
foo = x.Foo,
bar = x.Bar
};
foreach (Model item in list)
{
item.condition = "example";
}
return list;
}
public class Model{
public string foo{ get; set; }
public string bar { get; set; }
public string condition{ get; set; }
}
I case you dont want to load items in memory with a .ToArray or .ToList
You can use .Select from Linq.
return myEnumeration.Select(item => {
item.condition = "example";
return item;
})
I have a question regarding the filtering of an ObservableCollection (and its children).
I have the following class:
public class SomeClass
{
public string Description { get; set; }
public string CodeFlag { get; set; }
public double Price { get; set; }
public List<SomeClass> Children { get; set; }
public SomeClass()
{
this.Children = new List<SomeClass>();
}
public SomeClass Search(Func<SomeClass, bool> predicate)
{
// the node is a leaf
if (this.Children == null || this.Children.Count == 0)
{
if (predicate(this))
return this;
else
return null;
}
else // the node is a branch
{
var results = Children.Select(i => i.Search(predicate)).Where(i => i != null).ToList();
if (results.Any())
{
var result = (SomeClass)MemberwiseClone();
result.Children = results;
return result;
}
/*
* this is where I'm struggling
*/
return null;
}
}
}
And in the view model the following properties:
private ObservableCollection<SomeClass> originalDataSource;
public ObservableCollection<SomeClass> TreeViewDataSource { get; set; }
The originalDataSource is set in the constructor whilst the TreeViewDataSource is the collection bound to the TreeView.
I'm certain that there are better ways to accomplish this, (i.e. have just the one collection) but I'm happy with this for now.
Initially, all of the items in the collection are to be shown - I simply show the Description, Code and Price properties for each item, so far so good.
Now, the view model is informed that the current filter has changed so I want to be able to filter as such.
An example could be to show all items where “CodeFlag” is “ABC” or “XYZ”.
If the filter has changed, I set the TreeViewDataSource as such:
this.TreeViewDataSource = _getFilteredList(this.originalDataSource);
private ObservableCollection<SomeClass> _getFilteredList(ObservableCollection<SomeClass> originalList)
{
var filteredItems = new ObservableCollection<SomeClass>();
SomeClass filterResults = null;
switch (this.SelectedFilter)
{
case SomeEnum.SomeFilterOption:
filterResults = originalList[0].Search(x => x.CodeFlag.Equals("ABC") || x.CodeFlag.Equals("XYZ"));
break;
default:
break;
}
filteredItems.Add(filterResults);
return filteredItems;
}
This almost works as expected.
Where it is not working as expected is if an item has children where the filter does NOT apply.
In this scenario, even though the item itself matches the filter, as its children do not, null is returned.
The
/*
* this is where I'm struggling
*/
comment is where I believe I need additional logic.
Please note, the credit for the original Search method goes to #tono-nam
As it's the Weekend and I may be in a different time zone as that of the vast majority of you, please do not be offended if I do not respond straight away!
Have a great weekend!
You don't need an ObservableCollection for the items you're going to show, since the entire collection changes at once. You can simply use e.g. an array, and let the parent class implement INotifyPropertyChanged to notify the fact that the entire collection has changed.
To answer your question about what to return instead of null, use the same logic you use for leaves: return the item if it matches the predicate and null otherwise.
You can simplify your code by reordering the conditions: first get all children that satisfy the predicate, and if there are none (either because there are no children, or because there are children but they don't match - doesn't matter) then treat the collection as a leaf.
I have a list of object and I want to replace one of the objects in the list with the new object:
public Parent AppendParentChildren(Request request)
{
var Children = request.Parent.Children.ToList();
if (Children.Any(x => x.TrackingNumber == request.Child.TrackingNumber))
{
//Here I want to replace any Children that have the same tracking number in the list with the new Child passed in
}
else
{
Children.Add(request.Child);
}
request.Parent.Children = Children;
return request.Parent;
}
public class Request
{
public Parent Parent { get; set; }
public Child Child { get; set; }
}
public class Parent
{
public IEnumerable<Child> Children {get;set;}
}
If I try and use it in a loop:
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
foreach (var item in items)
{
//this
item = newItem;
}
}
}
item is read only, so I cannot replace the object in the list.
Any suggestions?
You can't change the member of a foreach iteration because foreach implements the IEnumerable type which is read-only.
A solution would be to cast the list of items inside the extension method as a List (which is read-writable). Then you'd want to identify which item(s) in the list you are replacing and update them. Below is what the Update extension method would look like (assuming you're in a situation where you can use LINQ)
public static class Extension
{
public static void Update<T>(this List<T> items, T newItem)
{
var childList = items as List<Child>;
var newChildItem = newItem as Child;
var matches = childList.Where(x => x.TrackingNumber == newChildItem.TrackingNumber).ToList();
matches.ForEach(x => childList[childList.IndexOf(x)] = newChildItem);
}
}
I've put a working example (albeit slightly bloated) on dotnetfiddle
https://dotnetfiddle.net/MJ5svP
It's also worth noting that although it looks like you're altering childList, this is actually referenced back to the original list not creating a copy (more info on this here)
I need to get a node of a nested collection looking through It's deep with linq.
This is the nested class:
public class Group
{
public int Id { get; set; }
public string Description { get; set; }
public int ParentId { get; set; }
public List<Group> Groups { get; set; }
}
Each instance of this class can have multiple instances inside the Groups method and so on. Each instance is linked through the ParentId property.
I need, having an instance of Group class, retrieve his father. I tried with this:
var parent = _repositoryGroups
.Where(g => g.Id == [my Group instance].ParentId)
.SelectMany(g => g.Groups)
.FirstOrDefault()
I don't know why, but not always it find the father instance and It starts looking from the second level (but this is not a real problem).
What's the best way to find the element through all the deep of this nested class?
Thanks
It now sounds like you want to get all childs recursively of a certain group.
So you can have:
private IEnumerable<Group> EnumerateChildren(Group parent)
{
if (parent.Groups != null)
{
foreach (var g in parent.Groups)
{
yield return g;
foreach (var sub in EnumerateChildren(g))
{
yield return sub;
}
}
}
}
If you just want to get the parent of a certain group:
private Group GetParent(Group child)
{
_repositoryGroups.Where(g => g.Id == child.ParentId).FirstOrDefault();
}
And if you need to get the super-parent of a certain group (parent of parent of parent of...):
private Group GetSuperParent(Group child)
{
parent = GetParent(child);
while (parent != null)
{
child = parent;
parent = GetParent(child);
}
return child;
}
Above it all, I recommend that if you can do that, hold a reference to the parent instead of it's Id. Have it null if it has no father. Saves a lot of trouble. A lot.
If you want to go up your structure and find the last parent of the parents, you can use this piece of code:
var group = [my Group instance];
while(group.ParentId > 0)
{
group = _repositoryGroups.First(g => g.Id == group.ParentId);
}
This assumes your IDs are higher than zero, and that an id>0 will always have a valid parent.
Sorry guys, maybe I didn't explain my question very well. I've developed this solution inspiring to yours answers:
private static Group GetGroupFather(IEnumerable<Group> groups, Group child)
{
foreach (var group in groups)
{
// try to nested search
var result = GetGroupFather(group.Groups, child);
if (result != null) return result;
// check in the current level
if (group.Id == child.ParentId)
return group;
}
return null;
}
If you have a better solution using linq, please let me know.
I'll try to explain this the best I can. I'm having quite a bit of difficulty trying to figure out this logic.
Basically, I have a collection that includes thousands of objects which are each made up of a Parent and a Child property.
So, roughly, this:
public class MyObject{
public string Parent { get; set; }
public string Child { get; set; }
}
What I'm trying to figure out is how to build this out into a plain TreeView control. I need to build the relationships but I can't figure out how to because they can be mixed. I can probably explain this better with what the tree should look like:
So if I have the following items inside of my collection:
0. Parent: "A", Child: "B"
1. Parent: "B", Child: "C"
2. Parent: "B", Child: "D"
I would want my tree to look this like:
-A
--B
---C
-A
--B
---D
-B
--C
-B
--D
How can I do this in C#? I would need it to support up to N relationships as we have some branches I would expect to reach about 50 nodes deep.
UPDATE
This problem actually turned out to be considerably more complex than I originally realized, given the requirement of repeating the entire tree for each path. I've simply deleted the old code as I don't want to add any further confusion.
I do want to keep it on record that using a recursive data structure makes this easier:
public class MyRecursiveObject
{
public MyRecursiveObject Parent { get; set; }
public string Name { get; set; }
public List<MyRecursiveObject> Children { get; set; }
}
You'll see very clearly why this is easier after reading the implementation code below:
private void PopulateTree(IEnumerable<MyObject> items)
{
var groupedItems =
from i in items
group i by i.Parent into g
select new { Name = g.Key, Children = g.Select(c => c.Child) };
var lookup = groupedItems.ToDictionary(i => i.Name, i => i.Children);
foreach (string parent in lookup.Keys)
{
if (lookup.ContainsKey(parent))
AddToTree(lookup, Enumerable.Empty<string>(), parent);
}
}
private void AddToTree(Dictionary<string, IEnumerable<string>> lookup,
IEnumerable<string> path, string name)
{
IEnumerable<string> children;
if (lookup.TryGetValue(name, out children))
{
IEnumerable<string> newPath = path.Concat(new string[] { name });
foreach (string child in children)
AddToTree(lookup, newPath, child);
}
else
{
TreeNode parentNode = null;
foreach (string item in path)
parentNode = AddTreeNode(parentNode, item);
AddTreeNode(parentNode, name);
}
}
private TreeNode AddTreeNode(TreeNode parent, string name)
{
TreeNode node = new TreeNode(name);
if (parent != null)
parent.Nodes.Add(node);
else
treeView1.Nodes.Add(node);
return node;
}
First of all, I realized that the dictionary will contain keys for intermediate nodes as well as just the root nodes, so we don't need two recursive calls in the recursive AddToTree method in order to get the "B" nodes as roots; the initial walk in the PopulateTree method already does it.
What we do need to guard against is adding leaf nodes in the initial walk; using the data structure in question, these are detectable by checking whether or not there is a key in the parent dictionary. With a recursive data structure, this would be way easier: Just check for Parent == null. But, a recursive structure is not what we have, so the code above is what we have to use.
The AddTreeNode is mostly a utility method, so we don't have to keep repeating this null-checking logic later.
The real ugliness is in the second, recursive AddToTree method. Because we are trying to create a unique copy of every single subtree, we can't simply add a tree node and then recurse with that node as the parent. "A" only has one child here, "B", but "B" has two children, "C" and "D". There needs to be two copies of "A", but there's no way to know about that when "A" is originally passed to the AddToTree method.
So what we actually have to do is not create any nodes until the final stage, and store a temporary path, for which I've chosen IEnumerable<string> because it's immutable and therefore impossible to mess up. When there are more children to add, this method simply adds to the path and recurses; when there are no more children, it walks the entire saved path and adds a node for each.
This is extremely inefficient because we are now creating a new enumerable on every invocation of AddToTree. For large numbers of nodes, it is likely to chew up a lot of memory. This works, but it would be a lot more efficient with a recursive data structure. Using the example structure at the top, you wouldn't have to save the path at all or create the dictionary; when no children are left, simply walk up the path in a while loop using the Parent reference.
Anyway, I guess that's academic because this isn't a recursive object, but I thought it was worth pointing out anyway as something to keep in mind for future designs. The code above will produce exactly the results you want, I've gone ahead and tested it on a real TreeView.
UPDATE 2 - So it turns out that the version above is pretty brutal with respect to memory/stack, most likely a result of creating all those IEnumerable<string> instances. Although it's not great design, we can remove that particular issue by changing to a mutable List<string>. The following snippet shows the differences:
private void PopulateTree(IEnumerable<MyObject> items)
{
// Snip lookup-generation code - same as before ...
List<string> path = new List<string>();
foreach (string parent in lookup.Keys)
{
if (lookup.ContainsKey(parent))
AddToTree(lookup, path, parent);
}
}
private void AddToTree(Dictionary<string, IEnumerable<string>> lookup,
IEnumerable<string> path, string name)
{
IEnumerable<string> children;
if (lookup.TryGetValue(name, out children))
{
path.Add(name);
foreach (string child in children)
AddToTree(lookup, newPath, child);
path.Remove(name);
}
// Snip "else" block - again, this part is the same as before ...
}
like rubens, I tried both, but a little better I think A Generic Tree Collection
this tree collection got some nice functionality build-in to move around the tree, go read the whole article
sample with the link above
Static Class Module1
{
public static void Main()
{
Common.ITree<myObj> myTree = default(Common.ITree<myObj>);
myObj a = new myObj("a");
myObj b = new myObj("b");
myObj c = new myObj("c");
myObj d = new myObj("d");
myTree = Common.NodeTree<myObj>.NewTree;
myTree.InsertChild(a).InsertChild(b).InsertChild(c).Parent.Parent.InsertNext(a).InsertChild(b).InsertChild(d).Parent.Parent.InsertNext(b).InsertChild(c).Parent.InsertNext(b).InsertChild(d);
Console.WriteLine(myTree.ToStringRecursive);
Console.ReadKey();
}
}
Class myObj
{
public string text;
public myObj(string value)
{
text = value;
}
public override string ToString()
{
return text;
}
}
would be exactly what you just showed
-A
--B
---C
-A
--B
---D
-B
--C
-B
--D
If I understand this correctly, what you're trying to do is take one tree and transform it into another. The transformation essentially takes each non-leaf-node in the input tree and creates a node for it (and its descendants) in the output tree.
First off, you'll be happier if you design a data structure for your nodes that is genuinely recursive:
public class Node
{
public Node Parent { get; private set; }
public IEnumerable<Node> Children { get; private set; }
public bool HasChildren { get { return Children.Count() > 0; } }
public Node()
{
Children = new List<Node>();
}
}
Your MyObject class represents parent/child relationships between string values. As long as you're able to implement a FindChildren() method that returns the child values for a given parent value, using this class to rationalize the parent/child relationships is straightforward:
public string Value { get; set; }
public static Node Create(string parentKey)
{
Node n = new Node();
n.Value = parentKey;
foreach (string childKey in FindChildren(parentKey))
{
Node child = n.Children.Add(Node.Create(childKey));
child.Parent = n;
}
return n;
}
It's simple to implement a property that returns a node's descendants:
public IEnumerable<Node> Descendants
{
get
{
foreach (Node child in Children)
{
yield return child;
foreach (Node descendant in child.Descendants)
{
yield return descendant;
}
}
}
}
To add a Node to a TreeView, you need two methods. (Note that these aren't methods of the Node class!) I've made them overloads, but an argument can be made for giving them different names:
public void AddNode(Node n, TreeView tv)
{
TreeNode tn = tv.Nodes.Add(n.Value);
tn.Tag = n;
foreach (Node child in n.Children)
{
AddNode(child, tn);
}
}
public void AddNode(Node n, TreeNode parent)
{
TreeNode tn = parent.Nodes.Add(n.Value);
parent.Tag = n;
foreach (Node child in n.Children)
{
AddNode(child, tn);
}
}
I'm setting the Tag on each TreeNode so that you can find your way back to the original Node.
So to initialize your TreeView from a list of top-level parent keys, you need a method like this:
public void PopulateTreeView(IEnumerable<string> parents, TreeView t)
{
foreach (string parentKey in parents)
{
Node n = Node.Create(parentKey);
AddNode(n, t);
foreach (Node descendant in n.Descendants)
{
if (n.HasChildren)
{
AddNode(descendant, t);
}
}
}
}
Edit:
I didn't quite understand how your MyObject class was working; I think I do now, and I've edited this accordingly.