38 Stimmen

LINQ: Wie erhält man Elemente aus einer inneren Liste in eine Liste?

Mit den folgenden Klassen (stark vereinfacht):

public class Child
{
    public string Label;
    public int CategoryNumber;
    public int StorageId;
}

public class Parent
{
    public string Label;
    public List<Child> Children = new List<Child>();
}

Und mit den folgenden Daten:

var parents = new List<Parent>();

var parent = new Parent() {Label="P1"};
parent.Children.Add(new Child() {Label="C1", CategoryNumber=1, StorageId=10});
parent.Children.Add(new Child() {Label="C2", CategoryNumber=2, StorageId=20});
parents.Add(parent);

parent = new Parent() {Label="P2"};
parent.Children.Add(new Child() {Label="C3", CategoryNumber=1, StorageId=10});
parent.Children.Add(new Child() {Label="C4", CategoryNumber=2, StorageId=30});
parents.Add(parent);

parent = new Parent() {Label="P3"};
parent.Children.Add(new Child() {Label="C5", CategoryNumber=3, StorageId=10});
parent.Children.Add(new Child() {Label="C6", CategoryNumber=2, StorageId=40});
parents.Add(parent);

Wie erhalte ich nun eine Liste von Kindern (mit CategoryNumber=2) aus der Liste der Eltern, die mindestens ein Kind mit CategoryNumber = 1 enthalten?

Ich kann das Folgende tun, aber es scheint nicht optimal zu sein:

var validParents = from p in parents
                   where p.Children.Any (c => c.CategoryNumber==1)
                   select p;
var selectedChildren = validParents.Select(p => from c in p.Children 
                                                where c.CategoryNumber == 2
                                                select c);

Das erhalte ich für selectedChildren:

  • IEnumerable<IEnumerable<Child>>
    • IEnumerable<Child>
      • C2 2 20
    • IEnumerable<Child>
      • C4 2 30

Ist es möglich, nur eine flache Liste zu haben, die die beiden Kinderelemente enthält, anstatt zwei Unterlisten? Wie würde es in LINQ übersetzt werden?

54voto

Scott Ivey Punkte 39470

Sie können ein paar Abfragen aneinanderreihen, indem Sie SelectMany y Wo .

var selectedChildren = (from p in parents
                       where p.Children.Any (c => c.CategoryNumber==1)
                       select p)
                       .SelectMany(p => p.Children)
                       .Where(c => c.CategoryNumber == 2);

// or...

var selectedChildren = parents
                         .Where(p => p.Children.Any(c => c.CategoryNumber == 1))
                         .SelectMany(p => p.Children)
                         .Where(c => c.CategoryNumber == 2);

45voto

Eric Lippert Punkte 628543

Scott Ich möchte nur darauf hinweisen, dass Sie diese Abfrage tatsächlich mit Abfragefortsetzungssyntax :

from parent in parents 
where parent.Children.Any (c => c.CategoryNumber==1)
select parent into p
from child in p.Children
where child.CategoryNumber == 2
select child

Beachten Sie, wie Mit "into" können Sie das Ergebnis einer Abfrage in die nächste Abfrage einfließen lassen. . Ziemlich raffiniert, oder?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X