Select the first object that match condition or an inner object

128 views Asked by At

I have a class that contains an inner list of the same class, For example:

class Foo 
{
   string SearchId;
   List<Foo> GroupedPackages
}

I want to return the first "foo" instance that fits a condition, it can be in the main instance or in the inner List.

This is what I have so far - it is a bit ugly but it works:

Package = response.lst.Where(p => p.SearchId == SearchId || 
                                 (p.GroupedPackages != null &&
                                  p.GroupedPackages.Any(m => m.SearchId==SearchId)))
                                                   .FirstOrDefault();
if (Package != null)
{
    if (Package.SearchId != SearchId) 
    {

        Package = Package.GroupedPackages.FirstOrDefault(m => m.SearchId == SearchId);
    }
 }

Where "response.lst" is a List of foo and Package is foo.

I want if possible to do it in a one line lambda expression

3

There are 3 answers

0
Tim Schmelter On BEST ANSWER

This selects the first Foo with the specified SearchId both on top level or in any of the Foo instances in GroupedPackages.

Package = response.lst
    .Concat(response.lst
        .Where(x => x.GroupedPackages != null)
        .SelectMany(x => x.GroupedPackages)
    )
    .FirstOrDefault(x => x.SearchId == SearchId); 
3
LInsoDeTeh On

I'd first add a function to Foo that checks the condition:

class Foo {
  public bool FulfillsCondition(int searchId) {
     return this.SearchId==searchId;
  }
}

And then add a function to find itself or the children

class Foo {
  public Foo FindItemToFulfillCondition(int searchId) {
     if (this.FulfillsCondition) return this;
     return GroupedPackages.FirstOrDefault(p => p.FulfillsCondition);
  }
}

Then you can just

response.lst.Where(p => p.FindItemToFulfillCondition != null);
5
Enigmativity On

You need to flatten your list first before searching it.

Here's how to flatten the list:

Func<IEnumerable<Foo>, IEnumerable<Foo>> flatten = null;
flatten = fs =>
    from f0 in fs
    from f1 in new [] { f0, }.Concat(flatten(f0.GroupedPackages))
    select f1;

So, if I start with this structure:

var foos = new List<Foo>()
{
    new Foo()
    {
        SearchId = "Y",
        GroupedPackages = new List<Foo>()
        {
            new Foo() { SearchId = "X" },
            new Foo() { SearchId = "Z" },
            new Foo()
            {
                SearchId = "W",
                GroupedPackages = new List<Foo>()
                {
                    new Foo() { SearchId = "G" },
                    new Foo() { SearchId = "H" },
                }
            }
        }
    }
};

I can flatten it like this:

flatten(foos);

The resulting enumerable is this:

result

So then it becomes simple to do this:

var result = flatten(foos).Where(f => f.SearchId == SearchId).FirstOrDefault();