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 }