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
It will return an empty enumerable. It won't be 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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With