2009-07-08 7 views
0

Considérons la hiérarchie de classe suivante:LINQ - Sélectionnez les valeurs correctes de la collection imbriquée

public class Foo 
{ 
public string Name { get; set; } 
public int Value { get; set; } 
} 
public class Bar 
{ 
public string Name { get; set; } 
public IEnumerable<Foo> TheFoo { get; set; } 
} 

public class Host 
{ 
    public void Go() 
    { 
    IEnumerable<Bar> allBar = //Build up some large list 
    //Get Dictionary<Bar, Foo> with max foo value 
    } 
} 

Ce que je voudrais faire à l'aide Linq2Objects est d'obtenir un KeyValuePair où pour chaque barre dans la collection allBBar nous sélectionnons les Foo avec la propriété Value maximale. Cela peut-il être fait facilement dans une seule déclaration LINQ?

Répondre

2

Bien sûr, bien que ma solution préférée utilise MaxBy de MoreLINQ:

var query = allBar.ToDictionary(x => x, // Key 
           x => x.TheFoo.MaxBy(f => f.Value)); 

Notez que ce sera en forme de poire si TheFoo est vide pour toute instance Bar.

+0

Et sans l'option MaxBy? –

+1

Ensuite, vous voudriez fondamentalement écrire votre propre implémentation de MaxBy :) Vous * pourriez * faire quelque chose d'horrible, mais ce serait beaucoup plus simple d'inclure MaxBy dans votre code. –

+0

Assez juste. ;) –

0

Juste pour ajouter au commentaire de Jon à propos de MaxBy en forme de poire si vous n'avez pas de foos, vous pouvez faire un OrderByDescendant et ensuite utiliser FirstOrDefault pour obtenir l'élément Max. Si la collection est vide, il retournera simplement null au lieu d'aller "en forme de poire".

var foobars = bars.ToDictionary(bar => bar, 
           bar => bar.TheFoo.OrderByDescending(foo => foo.Value).FirstOrDefault()); 

Je ne pense pas que ce ne serait pas aussi efficace que MaxBy, mais il serait plus robuste dans le cas d'une collection vide.

1

Une autre façon en utilisant des agrégats au lieu de OrderBy afin que déterminer le maximum Foo est O (n) au lieu de O (n log n):

var query = allBar.ToDictionary(
    bar => bar, 
    bar => bar.TheFoo.Aggregate(
     null, 
     (max, foo) => (max == null || foo.Value > max.Value) ? foo : max));