Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional selection in LINQ (select instead if empty)

Is there some "LINQ" way to have a conditional selection of data, i.e. select from another source if the first one was empty? One example is if you have a tree structure of items and you want to get some asset from either a root, or if it is empty, from it's children.

I have the following example:

IEnumerable<Item> items = ...;
// Item has a Assets property that returns IEnumerable<Asset>
// Item has a SubItems property that returns IEnumerable<Item>
//    i.e. other items with assets in them

// getting assets from a "main" item
var assets = item.Assets.Where(a => HasRelevantAsset(a));

// if there were no relevant assets in the "main" item
if (!assets.Any()) {
    // then reselect from "subitems" assets instead
    assets = item.SubItems.SelectMany(item => 
        item.Assets.Where(a => HasRelevantAsset(a)));
}

// HasRelevantAsset(Asset) is a static method that returns 
// true if it is the asset that is needed
like image 684
Spoike Avatar asked Apr 03 '12 08:04

Spoike


People also ask

Will Linq select return null?

It will return an empty enumerable. It won't be null.

Can ToList () return null?

If you have a Query that returns a empty set then ToList returns null. You would probably expect it to return an empty list (Count = 0), which is the case when using data providers for SQL Server.

What does LINQ query return?

By default, LINQ queries return a list of objects as an anonymous type. You can also specify that a query return a list of a specific type by using the Select clause.


1 Answers

I believe the LINQ way will look a bit ugly

var assets = item.Any(a=>HaRelevantAsset(a)) ? item.Where(a => HasRelevantAsset(a)) :
                     item.SubItems.SelectMany(item => 
                            item.Assets.Where(a => HasRelevantAsset(a)));

I would opt for the other variant, extension method

public static IEnumerable<Asset> SelectRelevantAssets(this Item item)
{
     var assetsInItemFound = false;
     foreach (var asset in item.Assets)
     {
         if (HasRelevantAsset(asset))
         {
             assetsInItemFound = true;
             yield return asset;
         }
     }
     if (assetsInItemFound)
     {
         yield break;
     }
     else
     {
         foreach (var subItem in item.SubItems)         
             foreach (var asset in subItem.Assets)
                if (HasRelevantAsset(asset))
                    yield return asset;
     }
}





First I wanted to try a Recursive call to SelectRelevantAssets, I think would be like

if (!assetsInItemFound)
         {
             yield break;
         }
         else
         {
             foreach (var subItem in item.SubItems)         
                 foreach (var asset in SelectRelevantAssets(subItem))
                        yield return asset;
         }

But this will include the assets found in the Items collection of assets of the subItem

like image 106
Adrian Iftode Avatar answered Oct 03 '22 15:10

Adrian Iftode