Home > Software design >  Filter list inside the list of objects
Filter list inside the list of objects

Time:04-29

I have a following list of objects:

List<Parent> parents = new();

Parent contains List<Children> list of children.

What I want is to filter list of parents and return female children for all parents. (and ignore/remove male children)

List<Parent> parentsAndTheirFemales = parents.Where(p => p.Children.Any(c => c.Gender == "female")).ToList();

I tried it with the above approach, but apparently it does not do the filtering.

I know I could remap it to DTO but let's consider that I have far too many properties on my parent class and I don't want to do that.

Is there another option?

EDIT: I settled with remapping.

parents.Select(FilterFemales());

static Func<Parent, Parent> FilterFemales()
        {
            return parent => new Parent()
            {
                Name = parent.Name,
                ... all properties
                Children = parent.Children.Where(c => c.Gender == "female").ToList()
            };
        }

CodePudding user response:

Assuming you want to return all parents that have girls, but only the girls included (hence the "Filter inside the list of objects" title) you can do this:

 var parentsWithTheirGirls = data.Where(parent => parent.Children.Any(child => child.Gender == "Female"))
                                 .Select(p => new Parent 
                                 { 
                                     Name = p.Name, 
                                     Children = GetChildren(p.Children, "Female") 
                                 }).ToList();

'GetChildren' is just another method to do the filtering like this:

static List<Child> GetChildren(IEnumerable<Child> children, string gender)
{
    return children.Where(child => child.Gender == gender).ToList();
}

You don't need to have the child filter in a separate method but I think it makes it more readable. I would also use an enum for the gender in place of a string.

CodePudding user response:

A more generic solution would be to extend your Parent class with a method that takes a predicate for the Children property. Perhaps do you at some point want to return parents with only the male children, without having to reimplement too much logic?

Example implementation:

public class Parent
{
    // Other properties
    public List<Child> Children { get; set; }
    
    public Parent CopyWithChildFilter(Func<Child, bool> childPredicate)
    {
        return new() 
        { 
            // Set other properties
            Children = Children.Where(childPredicate).ToList()
        };
    }
}

Example usage:

List<Parent> parents = new();

Func<Child, bool> isFemaleChild = c => c.Gender == "female";
//Func<Child, bool> isMaleChild = c => c.Gender == "male";

List<Parent> parentsAndTheirFemales = parents
    .Where(p => p.Children.Any(isFemaleChild))
    .Select(p => p.CopyWithChildFilter(isFemaleChild))
    .ToList();

Example fiddle here.

CodePudding user response:

Aren't you missing an equal sign in Gender = "Female"?

List<Parent> parentsAndTheirFemales = parents.Where(p => p.Children.Any(c => c.Gender = "female")).ToList();

should be

List<Parent> parentsAndTheirFemales = parents.Where(p => p.Children.Any(c => c.Gender == "female")).ToList();

or even better to avoid it use the .Equals function

CodePudding user response:

Maybe SelectMany is what you're looking for?

parentsAndTheirFemales.SelectMany(c => c.Children.Where(c => c.Gender == "female"));

CodePudding user response:

var filtered = parents;
foreach (var p in filtered)
{
    p.Children.RemoveAll(c => c.Gender == "Male");
}
filtered.RemoveAll(p => !p.Children.Any());

CodePudding user response:

I think this is what you are looking for;

 var girls = parents
      SelectMany(family => 
      family.Childs.Where(child => child.Gender == "Female"), 
      (parent, daughter) => new { parent = parent.Name, daughter = daughter.Name });

Have a look my small test:

public class Parent
    {
        public string Name { get; set; }
        public List<Child> Childs { get; set; }
    }

    public class Child
    {
        public string Name { get; set; }
        public string Gender { get; set; }
    }



var parents = new List<Parent>()
        {
            new Parent
            {
                Name = "Anderson",
                Childs = new List<Child>
                {
                    new Child
                    {
                        Name = "Alex",
                        Gender = "Male"
                    },
                    new Child
                    {
                        Name = "Maria",
                        Gender = "Female"
                    },
                    new Child
                    {
                        Name = "Sabrina",
                        Gender = "Female"
                    }

                }
            },
             new Parent
            {
                Name = "Clark",
                Childs = new List<Child>
                {
                    new Child
                    {
                        Name = "Mario",
                        Gender = "Male"
                    },
                    new Child
                    {
                        Name = "Marina",
                        Gender = "Female"
                    }

                }
            }

        };

          var girls = parents
      SelectMany(family => 
      family.Childs.Where(child => child.Gender == "Female"), 
      (parent, daughter) => new { parent = parent.Name, daughter = daughter.Name });
            .ToList();

        foreach (var item in girls)
        {
            Console.WriteLine(item);
        }

And this the result :

{ parent = Anderson, daughter = Maria }

{ parent = Clark, daughter = Marina }

{ parent = Clark, daughter = Sabrina }

  •  Tags:  
  • c#
  • Related