2012-04-03 2 views
6

Esiste un modo "LINQ" per avere una selezione condizionale di dati, ovvero selezionare da un'altra fonte se il primo era vuoto? Un esempio è se si ha una struttura ad albero di elementi e si desidera ottenere una risorsa da una radice o, se è vuota, dai suoi figli.Selezione condizionale in LINQ (selezionare invece se vuota)

ho il seguente esempio:, metodo di estensione

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 
+0

Questo sembra un buon uso per il ?? operatore - 'var assets = qualcosa ?? qualcosa_else'. Non funzionerà, ma sarebbe bello se lo facesse. – zmbq

risposta

1

Credo che il modo LINQ sarà un po 'brutto

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

opterei per l'altra variante

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; 
    } 
} 





In primo luogo ho voluto provare una chiamata ricorsiva a SelectRelevantAssets, penso che sarebbe come

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

Ma questo includerà i beni presenti nella collezione voci dell'attivo della sottovoce