learner
learner

Reputation: 541

Linq Get Specified Children and their Parents

I have two classess Parent and Child. I would like to get all parents whose children meet some criteria e.g all parents and their female children. I would like to do this using Linq. I have used the following Linq query and it returns all parents with female children, however the children list also contains male children. I would only like to have parent and their female children in List<Parent> parentsAndFemaleChildren. Please see the Linq query below:

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

The code for populating data is shown below.

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

I have populated the data as per below.

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("***********");
        }
    }

The full code can be seen and run via dotnet fiddle click here.

Upvotes: 4

Views: 801

Answers (3)

sommmen
sommmen

Reputation: 7660

This would work: 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
***********

You first fetch parents that have some female children at all, and then you filter out any male children, so you only display females.

EDIT:

Another flavor:

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

Edit2:

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

Outputs the same as above:

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

Upvotes: 2

StepUp
StepUp

Reputation: 38209

If you need to filter parents who have ONLY female children, then you should use ALL linq method.

MSDN says about .All method:

Determines whether all elements of a sequence satisfy a condition.

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

If you want to get parents who have at least one children with female gender and select just female children, then you can use select method with 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();

Upvotes: 1

Guru Stron
Guru Stron

Reputation: 143453

In latest version of language you can play with valutuples:

var parentsAndFemaleChildren = Parents
    .Select(p=> (parent: p, femaleChildren: p.Children.Where(c => c.Gender == "Female").ToList()))
    .Where(p => p.femaleChildren.Any())
    .ToList();

And print function:

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

Upvotes: 2

Related Questions