views:

111

answers:

4

I am working with a List<T> which contains both parent and children objects. In this list children objects are aware of their related parent object and vice versa. Using this list I am trying to implement a business rule where up to 4 children objects will be removed from the list when their parent is of a certain type. Put differently if a parent of this type has 20 children 4 of them should be removed from the list.

The code I have outlined here will RemoveAll of the children objects that meet the condition. This is expected but what I'd like to do is limit the RemoveAll to removing only 4 children. Is there a means to do this with RemoveAll or is there another method I should be using?

myList.RemoveaAll(item =>
  item.Child && "Foo".Equals(item.Parent.SpecialType));
A: 

Why not use the Take function?

Daniel A. White
Take does not modify the list. He wants to remove items.
Winston Smith
Take does not modify the list.
Daniel A. White
+5  A: 

The Take extension method is used to grab the first n number of matches from an IEnumerable. You can then iterate through the matches and remove them from the list.

var matches = myList.Where(item => item.Child && "Foo".Equals(item.Parent.SpecialType)).Take(someNumber).ToList();
matches.ForEach(m => myList.Remove(m));
Jake Pearson
To clarify, Take does not remove any items. You are using it to find the items which you subsequently wish to remove.
Winston Smith
There's only one problem: `Take()` doesn't iterate the list. So with the code as-is, you are iterating over myList while removing from the list. THIS IS BAD! I suggest you append `.ToList()` or `.ToArray()` to your first line. In fact, I'll go ahead and add it for you, feel free to rollback if you disagree.
Randolpho
With @Randolpho's modification this works like a charm. Otherwise a .ToList had to be appended to matches before using `ForEach`. I had never seen the `Take` extension method. Thank you for pointing it out!
ahsteele
@ashteel: adding `.ToList()` after the `Take()` call or appending it to `matches` accomplish the same thing; the only thing that changes is what the type of matches actually is in that short time. You could just as easily have done it in a single line: `myList.Where(item => item.Child `
Randolpho
+2  A: 

Does it matter which 4? If not, you can use .Take(4) to create a list of 4 children, then iterate through and Remove the 4...

IanR
+1  A: 

try this:

int i = 0;
myList.Removeall(item =>
  item.Child && "Foo".Equals(item.Parent.SpecialType) && i++ < 4);

Note that I haven't tested it but it should work

Jeff Hornby
Without testing this does `i` get reset to 0 upon each iteration? It seems like it would work for the first find but wouldn't afterwards.
ahsteele
I just tested it and it worked just fine
Jeff Hornby
Sweet that's almost more clear.
ahsteele