Reputation: 9101
I have a model that looks like the following:
public class MyType{
public string Id {get;set;}
public string Name{get;set;}
public List<MyType> Children{get;set;}
}
and in my data I have just two level data, meaning my objects will look like:
{
MyType{"1","firstParent",
{
MyType{"2","firstChild",null},
MyType{"3","secondChild",null}}
},
MyType{"4","secondParent",
{
MyType{"5","firstChild",null},
MyType{"6","secondChild",null}}
}
}
How do I query to get MyType object with a specific Id where it might be a parent or child?
The following will return only parents.
collection.FirstOrDefault(c => c.id==id)
Upvotes: 4
Views: 1122
Reputation: 249476
You can use Any
with a recursive local function to find objects on any level (your data structure would seem to indicate a deeper level is possible)
bool hasIdOrChildren(MyType t, string localId)
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
Or using pre C#7 syntax:
Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
If you are only interested in one level, you can drop the reclusiveness:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));
Edit
The code above gives the parent if any child has the id, you can also flatten the whole tree structure using SelectMany
also with a recursive function:
IEnumerable<MyType> flattenTree(MyType t)
{
if(t.Children == null)
{
return new[] { t };
}
return new[] { t }
.Concat(t.Children.SelectMany(flattenTree));
};
collection
.SelectMany(flattenTree)
.FirstOrDefault(c => c.Id == id);
This method can be useful for any type of processing where you need to flatten the tree.
Upvotes: 7
Reputation: 4527
I think, you should flatten collection
using SelectMany method, then use FirstOrDefault
to get element by id
:
MyType selected = collection
.SelectMany(obj => new MyType[] {obj, obj.NestedList})
.FirstOrDefault(obj => obj.id == id);
Upvotes: 1
Reputation: 1904
I think you're looking for
var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);
This flattens the list and gives back one list with all items in it. In your case you need to select
collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);
You still got the childs in your joined parents here, but you can still erase them or ignore them.
Upvotes: 1
Reputation: 299
You could build a list of all MyType including children and then query on it like this :
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)
Upvotes: 3