I have two methods to return a dynamic hierarchical structure from a flat List. The first works great using the recursive method here: (ID/ParentID) list to Hierarchical list.
I'm now trying to do the same thing except this time show only those categories and reports which have a saved report output. I'm not sure where to start as everything I find is building from root down and I need to go from the bottom up.
I get something like this now in my first method:
Category 1
|_Sub Category 1
|_Report 1
|_Report 2
|_Saved Output
Category 2
|_Sub Category 2
| |_Report 3
| |_Report 4
|_Sub Category 3
|_Report 5
|_Report 6
|_Saved Output
Category 3
|_Sub Category 4
|_Report 7
What I want in my second method is this:
Category 1
|_Sub Category 1
|_Report 2
|_Saved Output
Category 2
|_Sub Category 3
|_Report 6
|_Saved Output
Here's my basic test structure:
class Flat
{
public int id { get; set; }
public int parentId { get; set; }
public string name { get; set; }
public bool isOutput { get; set; }
public Flat(int i, int pid, string n, bool o)
{
this.id = i;
this.parentId = pid;
this.name = n;
this.isOutput = o;
}
}
class MyClass
{
public int id { get; set; }
public int parentId { get; set; }
public string name { get; set; }
public bool isOutput { get; set; }
public List<MyClass> children { get; set; }
public MyClass()
{
this.children = new List<MyClass>();
}
}
List<Flat> items = new List<Flat>()
{
new Flat(1,0,"Category 1",false),
new Flat(4,1,"Sub Category 1",false),
new Flat(8,4,"Report 1",false),
new Flat(9,4,"Report 2",false),
new Flat(15,9,"Saved Output",true),
new Flat(2,0,"Category 2",false),
new Flat(5,2,"Sub Category 2",false),
new Flat(10,5,"Report 3",false),
new Flat(11,5,"Report 4",false),
new Flat(6,2,"Sub Category 3",false),
new Flat(12,6,"Report 5",false),
new Flat(13,6,"Report 6",false),
new Flat(16,13,"Saved Output",true),
new Flat(3,0,"Category 3",false),
new Flat(7,3,"Sub Category 4",false),
new Flat(14,7,"Report 7",false)
};
To build from the bottom up, you need to start with all the leaf nodes that are valid (output == true), and then work upwards through all parent nodes until you reach the root. Here's one method that should work:
List<Flat> GetSavedOutput(List<Flat> items)
{
// get all output leaf nodes
var toAdd = items.Where (i => i.isOutput == true).ToList();
var result = new List<Flat>();
// grab all parent nodes that are not already included until
// there's nothing new to add
while (toAdd.Count > 0)
{
result.AddRange(toAdd);
toAdd = items.Where (i => !result.Contains(i)
&& result.Any (r => r.parentId == i.id)).ToList();
}
return result;
}
This is short and quick, and should work well for small, simple trees, but it is not the most efficient method because of processing the same nodes over and over again. A slightly more complex, but better method, would be to walk up the parent tree for each item:
List<Flat> GetSavedOutput(List<Flat> items)
{
var savedOutput = items.Where (i => i.isOutput == true).ToList();
var result = new List<Flat>();
foreach (var item in savedOutput) {
result.Add(item);
var temp = item;
do {
temp = items.Single (i => i.id == temp.parentId);
result.Add(temp);
} while (temp.parentId != 0);
}
return result;
}
If this is still not efficient enough, you can get a little more performance by storing references to the parent node in each Flat instance, so that the parent can be directly referenced in O(1) without having to look it up using a call to Single, which has efficiency O(n).
First I would suggest defining a recursive method to determine whether an item has a path to an output item, based on a given list:
static bool HasPathToOutput(List<Flat> items, Flat item)
{
if (item.isOutput)
{
return true;
}
// Recursively determine whether any of the item's children have
// a path to an output
return items.Where(i => i.parentId == item.id).Any(i => HasPathToOutput(items, i));
}
Then use that method to run your list through some LINQ queries, first getting the items that have a path to a saved output, then building the hierarchy, and finally, retrieving just the items that are at the top of their hierarchy:
// Generate a predicate based on the list
List<MyClass> foundItems =
items.Where(item => HasPathToOutput(items, item))
.Select(f => new MyClass { id = f.id, isOutput = f.isOutput, parentId = f.parentId, name = f.name })
.ToList();
// Generate child relationships
foundItems.ForEach(item => item.children = foundItems.Where(child => child.parentId == item.id).ToList());
// Filter out top-level items
List<MyClass> topLevel = foundItems.Where(i => i.parentId == 0).ToList();
You want to traverse your data depth-first order
This will look at all your leaf nodes first, add saved elements to the list and signal to the parent nodes that the root node was saved.
public bool StoreSavedElements(List<Tree> elements)
{
bool nodeSaved = false;
foreach (Tree child in childs)
{
if (child.StoreSavedElements(elements))
{
nodeSaved = true;
}
}
if (this.text == "Saved")
{
nodeSaved = true;
elements.Add(this);
}
return nodeSaved;
}
Related
I'm trying to update the Selectedproperty of an IEnumerable<SelectListItem> for a MVC-Combobox website using linq. However this is not working, as shown in the debbuging result: The Count() for the criteria returns an item, however the Count()for .Selected == truereturns 0.
public IEnumerable<SelectListItem> Categories { get; set; }
public CategoryModel Category
{
get { return category; }
set
{
category = value;
Categories.Where(x => x.Value == value.Id.ToString()).First().Selected = true;
}
//Debugging Results
//?Categories.Where(x => x.Value == value.Id.ToString()).Count()
//1
//?Categories.Count(x => x.Selected == true);
//0
}
Update:
I guess the problem is more bound to the IEnumerable<SelectListItem>, because the after changing Categories to an ObservableCollection it works fine (example below), even though LinQ is not designed for changing data....
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description = "Stackoverflow";
System.Diagnostics.Debug.Print(Categories.Where(x => x.Id == value.Id).FirstOrDefault().Description);
LINQ is to query your data-source not to modify it.
Your current approach has a drawback anyway, you would select one but you would not deselect the others. So you need a loop:
public CategoryModel Category
{
get { return category; }
set
{
category = value;
// consider to use a lock here to avoid multi threading issues
foreach(SelectListItem catItem in Categories)
catItem.Selected = catItem.Value == value.Id.ToString();
}
}
I would use a method SetSelectedCategory instead of a property if i'd modify a collection.
IEnumerable does not guarantee that changes get persisted across enumerations.
It all depends on the underlying implementation in the end (List, Array, Observable, etc).
Among the options that you have is to change your actual Categories to a writable collection (like List)...
But you might not be able to do that, or you might simply prefer to stay lean and keep using the IEnumerable.
In that case you could simply mutate the original collection and project it over the original
void Main()
{
Categories = Load();
var active = new Func<CategoryModel, int, CategoryModel>((category, match) =>
{
return new CategoryModel
{
Id = category.Id,
Name = category.Name,
Active = category.Id == match
};
});
Categories = Categories.Select(p => active(p, 2));
Categories.Dump();
}
public IEnumerable<CategoryModel> Categories { get; set; }
public IEnumerable<CategoryModel> Load()
{
yield return new CategoryModel { Id=1, Name = "one" };
yield return new CategoryModel { Id=2, Name = "two" };
yield return new CategoryModel { Id=3, Name = "three" };
}
public class CategoryModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
Id|Name|Active
1 one False
2 two True
3 three False
this is also to emphasize that you can use linq for "transformations" using "projections"
I have three lists of objects each of which are linked to one other list by a single common property, Parent and Node in the following hierarchy: Model -> Intermediate -> Results. I have the following code:
class Result
{
public string Name { get; set; }
public int Node{ get; set; }
public int Parent{ get; set; }
}
class Intermediate
{
public int Node{ get; set; }
public int Parent{ get; set; }
}
class Model
{
public string Name { get; set; }
public int Node{ get; set; }
public int Parent{ get; set; }
}
public class Example
{
public static void Main()
{
List<Result> results = new List<Result>();
List<Intermediate> intermediates = new List<Intermediate>();
List<Model> models = new List<Model>();
// Example objects in the list
results.Add(new Result() { Name = "", Parent = 21, Node = 101 });
intermediates.Add(new Part() { Parent = 11, Node = 21 });
models.Add(new Part() { Name = "ABCD", Parent = 1, Node = 11 });
...
}
}
As can be seen the Model object links to Intermediate object via model.Node and intermediate.Parent, and the Intermediate object links to Results object via intermediate.Node and results.Parent.
Note the lists can contain thousands of items, each added using a similar format as above.
What I want to be able to do is add the names from the Model objects in list to the matching Results objects in the results list.
My thinking is that I can loop through each object in the Intermediate list and find (using LINQ) the Result object where intermediate.Node = result.Parent, and then either replace the value of the result.Parent with intermediate.Parent, or add a new Grandparent property to the Result object in which to put the intermediate.Parent. Then repeat the process looping through each of the objects in the models list finding the matching Result object and adding the Name.
So I guess my question is, is this the best way of doing this, or is there a more efficient way? I have many lists where the same will have to be repeated, so was wondering if there was a better way as it can be quite slow looping through every object. Also is there a way to get from the first list directly to the third list.
I hope this is well enough explained. I am quite a beginner when it comes to C#.
You actually have Results -> Intermediate -> Model instead of Model -> Intermediate -> Results.
To speed the process of removing the Intermediate, build a dictionary. Then you can do a simple select on Results using the dictionary to convert.
var intermediateDict=intermediates.ToDictionary(key=>key.Node,val=>val.Parent);
var newresults=results.Select(r=>new Result {
Name=r.Name,
Node=r.Node,
Parent=intermediateDict[r.Parent]
});
You can also do joins to get the final answer.
It looks a foreach loop can be used to get to the result (leaf) nodes and assign the model name to result nodes:
var theModel = models.First(); // select a model
foreach (var interm in intermediates.Where(x => x.Parent == theModel.Node))
{
foreach (var res in results.Where(x => x.Parent == interm.Node))
{
res.Name = theModel.Name;
}
}
Smells like Composite Pattern what you talk about here.
And you can use HashSet to keep your values to perform it fast.
public class Item
{
public Item(int itemNode)
{
Node = itemNode;
Children = new HashSet<Item>();
}
public int Node { get; set; }
public Item Parent { get; set; }
private HashSet<Item> Children { get; set; }
public bool Add(Item item)
{
item.Parent = this;
return Children.Add(item);
}
public List<Item> Find(Func<Item, bool> predicate)
{
var found = new List<Item>();
if (predicate(this)) found.Add(this);
Collect(predicate, found);
return found;
}
public void Collect(Func<Item, bool> predicate, List<Item> collected = null)
{
collected = collected ?? new List<Item>();
collected.AddRange(Children.Where(predicate).ToList());
foreach (var child in Children)
{
child.Collect(predicate, collected);
}
}
}
public class Model : Item //this is your model
{
public string Name { get; set; }
public Model(int itemNode, string name) : base(itemNode)
{
Name = name;
}
public List<Item> GetNamesMatchingWith(Func<Item, bool> predicate)
{
return Find(predicate);
}
}
public class Example
{
public static void Main()
{
var root = new Model(0, "root");
var one = new Model(1, "1");
var two = new Model(2, "2");
var tree = new Model(3, "3");
root.Add(one);
root.Add(two);
root.Add(tree);
var a = new Model(4, "a");
var b = new Model(5, "b");
two.Add(a);
two.Add(b);
var namesMatchingWith = root.GetNamesMatchingWith(x=> x.Parent!=null && x.Parent.Node == 2);
Console.ReadKey();
}
}
Hope it inpires you..
I have an existing class :
public class Product
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Info { get; set; }
}
And I also have a given List<Product>:
var Lst = new List<Product>();
Lst.Add(new Product{ Id=1,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=2,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=3,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=60,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=61,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=62,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=72,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=90,ParentId=2,Info="a"});
Visualization :
1
|
+---60
|
+---61
|
+---62
|
+---72
2
|
+---90
3
As you can see , the List<> is flat. (all items are in the same level within the list. it's just that the id,parentId represents heirarchy)
Now - I need to create structural List<> so each item in List will be inside its parent object :
so I created an additional structure class which will hold this structure :
public class Node
{
public Product Product { get; set; }
public List<Node> LstNodes { get; set; }
}
So now I can do :
List<Node> lstNodes = new List<Node>();
And initially I can add the root ones :
lstNodes=Lst.Where(product=>product.ParentId==0).Select(node=>new Node{Product=node}).ToList();
And now I can start recursion to insert items with their parents.
So Where is the problem ?
Question:
I want to avoid insertion of root elements first ( root is where ParentId=0).
Is there any way of doing this with one recursive method (including roots) ?
Desired result : 3 nodes in lstNodes where each has its children recursively.
Something like this?
List<Node> GetNodes(List<Product> lst, int parentId = 0)
{
var childProducts = lst.Where(x=>x.ParentId == parentId);
return childProducts
.Select(x=> new Node { Product = x, LstNodes = GetNodes(lst, x.Id)}
.ToList();
}
And purely generic version:
class Node<T>
{
public T Item { get; set; }
public List<Node<T>> LstNodes { get; set; }
}
List<Node<T>> GetNodes<T>(List<T> lst, Func<T, int> idSelector, Func<T, int> parentIdSelector, int parentId = 0)
{
var childProducts = lst.Where(x=>parentIdSelector(x) == parentId);
return childProducts
.Select(x=> new Node<T> { Item = x, LstNodes = GetNodes<T>(lst, idSelector,parentIdSelector, idSelector(x))})
.ToList();
}
GetNodes(Lst,x=>x.Id,x=>x.ParentId, 0);
You can try this:
static List<Node> BuildTree(List<Product> lst, int topID)
{
var tree = Enumerable.Empty<Node>().ToList();
if (lst == null)
{
return tree;
}
foreach (var product in lst)
{
if (product.ParentId == topID)
{
Node node = new Node
{
Product = product,
LstNodes = BuildTree(lst.Where(p => p.ParentId == product.Id).ToList(), product.Id)
};
tree.Add(node);
}
}
return tree;
}
And you can call: List<Node> roots = BuildTree(Lst, 0);
The solutions proposed so far work, but iterate over the entire collection once per node.
This makes them O(n2) and not suitable for large collections (databases, ...).
Here is a solution running in linear time:
var products = new List<Product>();
products.Add(...);
...
var nodes = products.Select(p => new Node { Product = p }).ToList(); // 1
var nodeWithId = nodes.ToDictionary(n => n.Product.Id); // 2
var parentChildren = nodes.Where(n => n.Product.ParentId != 0)
.ToLookup(n => nodeWithId[n.Product.ParentId]); // 3
foreach (var pc in parentChildren) // 4
{
var parent = pc.Key;
var children = pc.ToList();
parent.LstNodes = children;
}
Explanation:
Create the nodes from the products.
Create a dictionary that allows us to easily find a Node with a given id
Create a lookup (dictionary with multiple values per key) that gives us the children of a given node.
Iterate over the lookup an associate the children with their parents
I have a flat list of categories as shown in the following classes
public class FlatCategoryList
{
public List<FlatCategory> Categories { get; set; }
}
public class FlatCategory
{
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
}
I'm trying to map my flat list of categories to a heirarical structure such as shown below:
public class HieraricalCategoryList
{
public List<Category> Categories { get; set; }
}
public class Category
{
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
public List<Category> ChildCategories { get; set; }
}
My question is, what is the best way to achieve this, given the fact that there could be an infinite number child tiers?
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var hieraricalCategoryList = new HieraricalCategoryList();
//Do something here to map the flat category list to the hierarichal one...
return hieraricalCategoryList;
}
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var categories = (from fc in flatCategoryList.Categories
select new Category() {
ID = fc.ID,
Name = fc.Name,
ParentID = fc.ParentID
}).ToList();
var lookup = categories.ToLookup(c => c.ParentID);
foreach(var c in categories)
{
// you can skip the check if you want an empty list instead of null
// when there is no children
if(lookup.Contains(c.ID))
c.ChildCategories = lookup[c.ID].ToList();
}
return new HieraricalCategoryList() { Categories = categories };
}
A very easy and highly performant way to make this transformation is to create a lookup in which you map ID values to the nodes that should be the children of that ID value. This lookup can be created in a single pass of the nodes. After that you can iterate through all of the nodes again assigning their child collection to be the value of their ID value in the lookup.
Note that this is simpler if the lookup maps to objects of the type you are converting to, not converting from.
var lookup = list.Categories
.Select(category => new Category()
{
ID = category.ID,
Name = category.Name,
ParentID = category.ParentID,
})
.ToLookup(category => category.ParentID);
foreach (var category in lookup.SelectMany(x => x))
category.ChildCategories = lookup[category.ID].ToList();
var newList = new HieraricalCategoryList()
{
Categories = lookup[null].ToList(),
};
Improved the suggested answer
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var categories = (from fc in flatCategoryList.Categories
select new Category() {
ID = fc.ID,
Name = fc.Name,
ParentID = fc.ParentID
}).ToList();
var lookup = categories.ToLookup(c => c.ParentID);
foreach(var c in rootCategories)//only loop through root categories
{
// you can skip the check if you want an empty list instead of null
// when there is no children
if(lookup.Contains(c.ID))
c.ChildCategories = lookup[c.ID].ToList();
}
//if you want to return only root categories not all the flat list
//with mapped child
categories.RemoveAll(c => c.ParentId != 0);//put what ever your parent id is
return new HieraricalCategoryList() { Categories = categories };
}
Use a two-pass solution. This assumes the full collection can fit in memory. The first pass scans the list of flat categories, and builds a dictionary of Category, indexed by the ID. The child collections are all empty at this point, and the parent property is null. Then the second pass scans them again, and builds up the child collections and sets the parent property.
Untested code:
var final = new Dictionary<string, Category>();
var rootCategories = new List<Category>();
// Pass 1
foreach (var flat in flatList)
{
Category cat = new Category() { ID = flat.ID, Name = flat.Name, parent = null }
cat.Children = new List<Category>();
final[flat.ID] = cat;
}
// Pass 2
foreach (var flat in flatList)
{
// find myself -- must exist
var self = final[flat.ID];
// find parent -- may not exist
if (final.ContainsKey(flat.ParentID)
{
var parent = final[flat.ParentID];
parent.Children.Add(self);
self.Parent = parent;
}
else
{
rootCategories.Add(self);
}
}
This will have O(n) running time, since it's two linear scans, with some dictionary lookups, which are O(1).
I'm pulling a dataset into a c# list and to sort it. It's a hierarchical menu:
sample object:
public class NavigationInfo
{
public Int32 Id { get; set; }
public Int32 ParentId { get; set; }
public String Text { get; set; }
public String Url { get; set; }
public Int32 Sort { get; set; }
}
The ParentId is recursive to Id and Sort is an ascending integer within the ParentId. How is that done using a collection of NavigationInfo in List<NavigationInfo>?
You can do something like:
var navigationInfos = new List<NavigationInfo>(); //fill this collection
navigationInfos.sort((a,b) => a.Id.CompareTo(b.Id)); //sort by Id
navigationInfos.sort((a,b) => a.ParentId.CompareTo(b.ParentId)); //sort by ParentId
UPDATE: You can also use LINQ and do an OrderBy on the List. This returns a new collection, but is a lot easier to order by multiple criteria, ascending or descending.
var navigationInfos = new List<NavigationInfo>(); //fill this collection
var listSortedById = navigationInfos
.OrderBy(n => n.Id).ToList();
var listSortedByParentId = navigationInfos
.OrderBy(n => n.ParentId).ToList();
var listSortedByIdThenByParentId = navigationInfos
.OrderBy(n => n.Id)
.ThenBy(p => p.ParentId)
.ToList();
var orderedByIdDescending = navigationInfos
.OrderByDescending(n => n.Id)
.ToList();
If the data comes from DB, you can let the DB send the output in sorted manner (parent first, followed by child Id).
Note: I am assuming DB has the Ids in an order where parent will be a lesser Id than a child.
If your hierarchy will be more than a simple two-level arrangement, you'll need to write a method (probably recursive) that can travel up the tree collecting the entire "path" of each item's ids. Once you have this path, the sorting step is fairly easy.
Add a property to NavigateInfo
public string ItemPath { get; set; }
Then use a method like this to set that property.
public string GetPath(List<NavigationInfo> list, int itemId)
{
NavigationInfo item = list.SingleOrDefault(x => x.Id == itemId);
if (item == null)
{
return "";
}
else
{
return GetPath(list, item.ParentId) + "\\" + itemId;
}
}