Linq 获取指定子级以及它们的父级

4

我有两个类 Parent 和 Child。我想要获取所有孩子符合某些条件的父母,例如所有父母和他们的女儿。我想使用 Linq 进行此操作。我已经使用以下 Linq 查询,并返回所有有女儿的父母,但 children 列表中也包括男孩子。我只想在 List<Parent> parentsAndFemaleChildren 中包含父母和他们的女儿,请参见下面的 Linq 查询:

```csharp var parentsAndAllChildren = parentList.Where(p => p.Children.Any(c => c.Gender == Gender.Female)).ToList(); ```
请帮我修改这个查询来获得我所需的结果。
List<Parent> parentsAndFemaleChildren = Parents
    .Where(p => p.Children.Any((c => c.Gender == "Female")))
    .ToList();

以下是填充数据的代码。

public class Parent
{
    public int ParentId {get;set;}
    public string ParentName {get;set;}
    public List<Child> Children {get;set;}
}
public class Child
{
    public int ChildId {get;set;}
    public string ChildName {get;set;}
    public string Gender {get;set;}
    public int ParentId {get;set;}
}

我已按如下方式填充数据。

public static void Main()
    {
        List<Parent> Parents = new List<Parent>();

        Parent p1 = new Parent();
        p1.ParentId = 1;
        p1.ParentName = "Parent 1";
        Parents.Add(p1);    


        Child p1C1 = new Child();
        p1C1.ChildId = 1;
        p1C1.ChildName = "Parent 1 Child 1";
        p1C1.Gender = "Male";
        p1C1.ParentId = 1;

        p1.Children = new List<Child>();

        p1.Children.Add(p1C1);

        Child p1C2 = new Child();
        p1C2.ChildId = 2;
        p1C2.ChildName = "Parent 1 Child 2";
        p1C2.Gender = "Female";
        p1C2.ParentId = 1;

        p1.Children.Add(p1C2);

        Parent p2 = new Parent();
        p2.ParentId = 2;
        p2.ParentName = "Parent 2";
        Parents.Add(p2);

        Child p2C1 = new Child();
        p2C1.ChildId = 3;
        p2C1.ChildName = "Parent 2 Child 1";
        p2C1.Gender = "Female";
        p2C1.ParentId = 2;

        p2.Children = new List<Child>();

        p2.Children.Add(p2C1);

        Child p2C2 = new Child();
        p2C2.ChildId = 4;
        p2C2.ChildName = "Parent 2 Child 2";
        p2C2.Gender = "Male";
        p2C2.ParentId = 2;

        p2.Children.Add(p2C2);

        Parent p3 = new Parent();
        p3.ParentId = 3;
        p3.ParentName = "Parent 3";
        Parents.Add(p3);

        Child p3C1 = new Child();
        p3C1.ChildId = 5;
        p3C1.ChildName = "Parent 3 Child 1";
        p3C1.Gender = "Male";
        p3C1.ParentId = 3;

        p3.Children = new List<Child>();

        p3.Children.Add(p3C1);


        DisplayFemaleChildrenAndParents(Parents);
    }

    public static void DisplayFemaleChildrenAndParents(List<Parent> Parents)
    {
        List<Parent> parentsAndFemaleChildren = Parents.Where(p => p.Children.Any((c => c.Gender == "Female"))).ToList();
        Console.WriteLine("\nShould Display Parents and their Female children details ONLY\n");
        foreach(Parent p in parentsAndFemaleChildren)
        {       
            Console.WriteLine("***********");           
            foreach(Child c in p.Children)
            {               
                Console.WriteLine(p.ParentName + " - " + c.ChildName + " - " + c.Gender);
            }
            Console.WriteLine("***********");
        }
    }

完整代码可以通过 dotnet fiddle 查看和运行,点击这里

3
我喜欢带有可行示例的问题。 - sommmen
3个回答

2
这样就可以了:https://dotnetfiddle.net/ckwCs1
    public static void DisplayFemaleChildrenAndParents(List<Parent> Parents)
    {
        List<Parent> parentsAndFemaleChildren = Parents.Where(p => p.Children.Any((c => c.Gender == "Female"))).ToList();
        Console.WriteLine("\nShould Display Parents and their Female children details ONLY\n");
        foreach(Parent p in parentsAndFemaleChildren)
        {       
            Console.WriteLine("***********");           
            foreach(Child c in p.Children.Where(c=>c.Gender == "Female"))
            {               
                Console.WriteLine(p.ParentName + " - " + c.ChildName + " - " + c.Gender);
            }
            Console.WriteLine("***********");
        }
    }

***********
Parent 1 - Parent 1 Child 1 - Male
Parent 1 - Parent 1 Child 2 - Female
***********
***********
Parent 2 - Parent 2 Child 1 - Female
Parent 2 - Parent 2 Child 2 - Male
***********
***********
Parent 3 - Parent 3 Child 1 - Male
***********

Should Display Parents and their Female children details ONLY

***********
Parent 1 - Parent 1 Child 2 - Female
***********
***********
Parent 2 - Parent 2 Child 1 - Female
***********

首先,您需要获取拥有至少一个女性子女的父母,然后过滤掉任何男性子女,这样您就只显示女性了。

编辑:

另一种方式:

https://dotnetfiddle.net/sFObju

    public static void DisplayFemaleChildrenAndParents(List<Parent> Parents)
    {
        List<Parent> parentsAndFemaleChildren = Parents.Where(p => p.Children.Any((c => c.Gender == "Female"))).ToList();
        Console.WriteLine("\nShould Display Parents and their Female children details ONLY\n");
        foreach(Parent p in parentsAndFemaleChildren)
        {       
            Console.WriteLine("***********");           
            foreach(Child c in p.Children.Where(c=>c.Gender == "Female"))
            {               
                Console.WriteLine(p.ParentName + " - " + c.ChildName + " - " + c.Gender);
            }
            Console.WriteLine("***********");
        }

                    Console.WriteLine("\n\n ***********   v2 ***********   \n\n");


        // alternative
        var parentsWithFemales = Parents.Select(p => new {p, FemaleChildren = p.Children.Where(a => a.Gender == "Female")})
                                        // Filter out parents without female children
                                        .Where(x => x.FemaleChildren.Any());
        foreach(var pc in parentsWithFemales)
        {
            Console.WriteLine(pc.p.ParentName + "\tfemale children:");
            foreach(var c in pc.FemaleChildren)
                Console.WriteLine("\t"+c.ChildName + ", Gender: " + c.Gender);
        }

    }

***********
Parent 1 - Parent 1 Child 1 - Male
Parent 1 - Parent 1 Child 2 - Female
***********
***********
Parent 2 - Parent 2 Child 1 - Female
Parent 2 - Parent 2 Child 2 - Male
***********
***********
Parent 3 - Parent 3 Child 1 - Male
***********

Should Display Parents and their Female children details ONLY

***********
Parent 1 - Parent 1 Child 2 - Female
***********
***********
Parent 2 - Parent 2 Child 1 - Female
***********


 ***********   v2 ***********   


Parent 1    female children:
    Parent 1 Child 2, Gender: Female
Parent 2    female children:
    Parent 2 Child 1, Gender: Female

编辑2:

https://dotnetfiddle.net/N8K0AF

        var parentsWithFemales = Parents.Select(p => new Parent(){ParentId = p.ParentId, ParentName = p.ParentName, Children = p.Children.Where(a => a.Gender == "Female").ToList()})
                                        // Filter out parents without female children
                                        .Where(x => x.Children.Any());
        foreach(var pc in parentsWithFemales)
        {
            Console.WriteLine(pc.ParentName + "\tfemale children:");
            foreach(var c in pc.Children)
                Console.WriteLine("\t"+c.ChildName + ", Gender: " + c.Gender);
        }

输出与上述相同:

Parent 1    female children:
    Parent 1 Child 2, Gender: Female
Parent 2    female children:
    Parent 2 Child 1, Gender: Female

foreach循环仅用于演示目的。我希望List<Parent> parentsAndFemaleChildren变量包含至少一个女性的父母,而parentsAndFemaleChildren.Children仅包含女儿,不使用匿名类型。版本1使用foreach循环,版本2使用匿名类型。对于匿名类型,我应该更清楚,但我假设以下短语已经被理解:“我只想在List<Parent> parentsAndFemaleChildren中拥有父母和他们的女儿”。但我应该更清晰明了。 - learner
@learner 编辑了答案,包括创建一个新的父对象。 - sommmen

2
在最新版本的语言中,您可以使用valutuples进行操作:
var parentsAndFemaleChildren = Parents
    .Select(p=> (parent: p, femaleChildren: p.Children.Where(c => c.Gender == "Female").ToList()))
    .Where(p => p.femaleChildren.Any())
    .ToList();

还有打印函数:

    foreach(var (p, children) in parentsAndFemaleChildren)
    {       
        Console.WriteLine("***********");           
        foreach(Child c in children)
        {               
            Console.WriteLine(p.ParentName + " - " + c.ChildName + " - " + c.Gender);
        }
        Console.WriteLine("***********");
    }

在Linq中,我可以说匿名类型被更多地使用,所以我甚至没有想到元组也可以使用。现在我想知道哪个更好... - sommmen
你可以使用匿名类型来完成相同的操作。至于我更喜欢 valuetuples,因为它们应该分配较少的内存。 - Guru Stron
你认为它们在使用linq2sql时会表现得一样吗? - sommmen
1
实际上我从来没有尝试过。但是如果您不在物化结果中使用它们(如果底层提供程序支持元组),则它们之间没有区别,并且使用匿名类型也是可以的。 - Guru Stron
foreach循环仅用于演示目的。我希望List<Parent> parentsAndFemaleChildren变量包含至少一个女性的父母,而parentsAndFemaleChildren.Children仅包含女儿,而不使用匿名类型。 - learner
1
@learner,那么你需要创建新的“Parent”对象,其中只包含女孩子。就像“.Select(p => new Parent{Id = p.Id, Name = p.Name, Children = p.Children.Where(....).ToList()})”这样。 - Guru Stron

1
如果您需要筛选只有女性孩子的父母,则应使用ALL linq方法。

MSDN关于.All方法的说明:

确定序列的所有元素是否满足条件。

List<Parent> parentsAndFemaleChildren = Parents
    .Where(p => p.Children.All((c => c.Gender == "Female"))).ToList();

如果您想获取至少有一个女性孩子的父母,并选择仅女性孩子,则可以使用 select 方法和 Where
List<Parent> parentsAndFemaleChildren = Parents
    .Where(p => p.Children.Any((c => c.Gender == "Female")))
    .Select(s => new Parent { ParentId = s.ParentId, ParentName = s.ParentName, 
        Children = s.Children.Where(ch => ch.Gender == "Female").ToList()}).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接