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.
Related
I have a list of objects within a list of objects (List-ParentClass) that has as one of its objects a nested list (List-ChildClass). To populate List-ChildClass I have used a foreach loop as shown below. I have also nested a linq query as show below.
At this point I am having some performance issues and I feel like there is a better way to do this that I am just not finding.
Question: How could I do this better/faster?
Note - This is a Web based .net MVC application written in C#. I use EF back to a SQL database.
public class ParentClass
{
public int pcid { get; set; }
public List<ChildClass> ChildClassList { get; set; }
}
public class ChildClass
{
public int pcid { get; set; }
public int ccid { get; set; }
}
public class DoWork
{
public void ExampleMethodForEach()
{
List<ParentClass> ParentClassList = new List<ParentClass>();
foreach(ParentClass a in ParentClassList)
{
a.ChildClassList = EFDatabase2.where(b => b.pcid == a.pcid).select(b => b.ccid).ToList();
}
}
public void ExampleMethodLinq()
{
var ParentClassList = (from a in EFDatabase
select new ParentClass
{
ccid = a.ccid,
pcid = (from b in EFDatabase2
where b.pcid == a.pcid
select b.ccid).ToList()
//something like this were I nest a query
}).ToList();
}
}
The best way when working with relational databases and LINQ is to use joins to correlate data. In your case, the most appropriate is group join:
var ParentClassList =
(from p in EFDatabase
join c in EFDatabase2 on p.pcid equals c.pcid into children
select new ParentClass
{
pcid = p.pcid,
ChildClassList =
(from c in children
select new ChildClass
{
pcid = c.pcid,
ccid = c.ccid
}).ToList()
}).ToList();
which should give you a nice fast single database query.
P.S. Hope your EFDatabase and EFDatabase2 variables refer to two tables inside one and the same database.
You are hitting your database multiple times. You have a N+1 issue.
What I suggest is to query all parents first, but excluding the children data. Then get the ID of all parents that you retrieved and put it inside an array. We will use that array to create a IN clause in SQL.
After loading all the children using the array of parent IDs, map them to a Lookup using ToLookup using the parent ID as the key and use a foreach to assign the list of children to the parent.
var parents = EFDatabase2.Parents.Where(...).Select(p => new ParentClass { pcid = p.pcid }).ToList();
var ids = parents.Select(p => p.pcid).ToArray();
var children = EFDatabase2.Children.Where(c => ids.Contains(c.ccid)).Select(c => new ChildClass { pcid = c.pcid, ccid = c.ccid }).ToLookup(c => c.pcid);
foreach (var parent in parents)
{
parent.Children = children[parent.pcid];
}
In this case, you will only do two queries to your database.
I have the following basic classes (cut down for this question):
public class Parent
{
public string Name { get; set; }
public IList<Child> Children { get; set; }
}
public class Child
{
public string Name { get; set; }
}
If I have a Parent collection, what I'd like to do is get an IList that is sorted by Parent.Name and also the Children for each parent need to be sorted by their Name.
I've tried this (which only sorts the Parents, not the Children):
IList<Parent> parents = ... //Populated
parents.OrderBy(p => p.Name).ThenBy(p => p.Children.OrderBy(c => c.Name)).ToList()
I've searched but can't find anything (probably me being dumb).
Any suggestions for a Linq newbie?
Thanks in advance
Andy
First of all, calling OrderBy on the list, the way you do, won't sort it in-place. It will return a new sorted IEnumerable; you can use .ToList() on that to turn it into a list, but it will still be a copy. Now on to the sorting itself. You really need to not just order the items in the collection, but make a copy of each item which would have its Children sorted as well. So:
IList<Parent> parents = ... //Populated
parents = (from p in parents
orderby p.Name
select new Parent
{
Name = p.Name,
Children = p.Children.OrderBy(c => c.Name).ToList()
}
).ToList();
Same solution, using LINQ method syntax:
IList<MyType> myTypeList = ... //Populated
var sortedList = myTypeList.Select(t =>
{
t.Children = t.Children.OrderBy(c => c.Name).ToList();
return t;
}).ToList();
I have two entities:
public class Parent
{
public virtual int Id { get; set; }
}
public class Child
{
public virtual int Id { get; set; }
public virtual int ParentId
{
get
{
if (Parent != null)
return Parent.Id;
return -1;
}
set
{
if (Parent != null)
Parent = new Parent();
Parent.Id = value;
}
}
protected virtual Parent Parent
{
get;
set;
}
}
The Parent property is set up like this to simplify the API side, and I do not want to change it to expose this property publicly. I have an override of the mappings for the Child class to accommodate this:
public class ChildMappingOverrides : IAutoMappingOverride<Child>
{
public void Override(AutoMapping<Child> mapping)
{
mapping.References<Parent>(Reveal.Member<Child>("Parent")).Column("Parent_id");
mapping.IgnoreProperty(x => x.ParentId);
}
}
Now, if I want to query all the Child objects for a given parent Id, I would perform this:
session.QueryOver<Child>().Where(c => c.ParentId == 1);
However, this throws a QueryException:
could not resolve property: ParentId of: My.Namespace.Child
How can I retrieve the set of Child objects that have a particular Parent Id?
Untested, but you could try this:
session.QueryOver<Child>()
.Where(Restrictions.Eq(
Projections.SqlProjection(
"{alias}.Parent_id as ParentId",
new[] { "ParentId" },
new[] { NHibernateUtil.Int32 }), 1))
.List<Child>();
You're not going to be able to query on unmapped associations in any NHibernate query, but this at least minimizes the loss of compile-time checking.
This is somewhat limited from what I can tell. {alias} will always be replaced by the alias of the root entity, meaning if you want to do this for a more complex query that doesn't start with Child, you might be out of luck.
I've solved it by using CreateSQLQuery instead of QueryOver:
session.CreateSQLQuery("SELECT C.* FROM CHILD C WHERE C.Parent_id = (:id)")
.AddEntity(typeof(Child))
.SetInt32("id", parentId)
.List<Child>();
I'd like to see a better way if possible, losing the compile-time checking is kind of a downer.
I'm trying to update an item inside my object graph which could span 'n' level in depth.
The following is my object model:
public class Entity
{
public string Name { get; set; }
}
public class Category : Entity
{
public List<Category> Categories { get; set; }
public List<Product> Products { get; set; }
}
public class Product : Entity
{
}
My view is bound to an ObservableCollection<Category> Categories. What I want to do is given a category name, I need to retrieve the first object which matches that from the collection.
For ex, given a list like this and a category Facial Tissue, I need to retrieve the Facial Tissue category object from the collection.
Category - Pharmacy
|-Product - Aspirin
|-Product - Tylenol
|-Category - Tooth Paste
| |-Product - Crest
| |-Product - Colgate
|-Category - Paper Products
|-Category - Toilet Paper
| |-Product - NoName
| |-Product - Charmin
|-Category - Facial Tissue
|-Product - Kleenex
Category - Household
|-Product - Pinesol Cleaner
|-Product - Garbage Bags
I have tried this, but it throws an Object reference not set to an instance of an object exception when I search level >2 in the hierarchy.
return Categories.FirstOrDefault(n => n.Name == name) ??
Categories.SelectMany(node => node.Categories).Where(lx => lx.Name == name).FirstOrDefault();
NOTE: At times the categories could be null deep down the hierarchy.i.e if there are no categories, then the collection is set to null.Also the solution necessarily need not be using LINQ.
You can use either of the following methods to traverse the tree structure recursively:
public static IEnumerable<T> Traverse<T>(IEnumerable<T> source, Func<T, IEnumerable<T>> childSelector)
{
var queue = new Queue<T>(source);
while (queue.Any())
{
var item = queue.Dequeue();
yield return item;
foreach (var child in childSelector(item))
{
queue.Enqueue(child);
}
}
}
public static IEnumerable<T> Traverse<T>(T root, Func<T, IEnumerable<T>> childSelector)
{
return Traverse(new[] { root }, childSelector);
}
There is an overload for a single root item, and another that takes a sequence of items.
You could implement them using actual recursion if you prefer, but I prefer an explicit data structure. If you would like a depth first search instead of a breath first search just change the Queue to a Stack and update the methods accordingly.
To use it you can do something like this:
Category root = new Category();
var searchResult = Traverse(root, item => item.Categories)
.Where(category => category.Name == "testValue")
.FirstOrDefault();
It also appears that you're getting null errors because you have Categories that are null. If at all possible, I would highly encourage you to fix that problem, rather than dealing with it. If an Entity has no Categories it should have an empty list, not a null list. Having said that, you can adjust the Traverse calls as follows if you have any null items:
Traverse(root, item => item.Categories ?? Enumerable.Empty<Category>())
LINQ by itself does not have a dedicated operator for a depth-first search (which is what you need in this case). However, given your requirements, there is a fairly easy solution using a simple recursive function:
// Returns the first category with the given name or null, if none is found
Category findCategory(Category start, String name) {
if (start.name == name) {
return start;
}
if (start.Categories == null) {
return null;
}
return (from c in start.Categories
let found = findCategory(c, name)
where found != null
select found).FirstOrDefault()
}
You might consider setting the Categories property of categories with no subcategories to an empty list instead of null. This allows you skip the null check here (and probably also in a lot of other places).
Here is a simple solution although it doesn't only use Linq:
public Category GetCategory(string name, List<Category> Categories)
{
Category found = Categories.FirstOrDefault(cat => cat.Name == name);
return found ?? Categories.Select(cat => GetCategory(name,cat.Categories))
.FirstOrDefault(cat => cat != null);
}
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