Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# - query nested type with LINQ

Tags:

c#

linq

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)
like image 944
Yahya Hussein Avatar asked Dec 14 '22 17:12

Yahya Hussein


1 Answers

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.

like image 152
Titian Cernicova-Dragomir Avatar answered Jan 03 '23 04:01

Titian Cernicova-Dragomir