Remove all matching nodes in a list - c#

There are two things wrong with my script that i need help understanding.
The if statement looks for matching values in the original list. Then removes it. This doesn't work for some reason. the items.Remove(item) doesn't actually remove the item.
The else statement works. But i have a feeling that i can do this all in one line.
Thoughts? If possible I would like to avoid making it a list. Its originally an ObservableCollection.
var items = TreeViewCollection.ToList();
if (items.Contains(SelectedTreeViewItem))
items.Remove(SelectedTreeViewItem);
else
items.ForEach(e=>e.Nodes.Remove(SelectedTreeViewItem));
UPDATE: Ive updated the code to make a little more sense.
My goal is to make this into one line.
if (TreeViewCollection.Contains(SelectedTreeViewItem))
TreeViewCollection.Remove(SelectedTreeViewItem);
else
TreeViewCollection.ToList().ForEach(e=>e.Nodes.Remove(SelectedTreeViewItem));

Creating a new list is counterproductive. SelectedTreeViewItem will be removed from list but not from original collection.
ForEach is not an extension method for IEnumerable, it is a common method of List<T> class. It returns void, so it can only be in the end of Linq methods chain. Using foreach makes code simpler and clearly states its intent:
bool del = TreeViewCollection.Remove(SelectedTreeViewItem);
if (false == del)
foreach(var t in TreeViewCollection)
t.Nodes.Remove(SelectedTreeViewItem);

Related

What is the proper way to iterate through an IObservableList<T>?

I'm working on a WPF application that utilizes ReactiveUI and DynamicData. Most of our lists are of the type IObservableList and at times we need to subscribe to changes on this list, and at times we need to simply iterate through the list. My concern is around if we're following the correct pattern for iterating through these lists.
Currently, when we need to iterate through a list, we follow the following pattern:
// Assuming we have an IObservableList<SomeObject> named objList with some data in it
foreach (var obj in objList.Items)
{
// some operation on obj
}
This pattern works fine, but we're concerned that this isn't a "Reactive" way to do this. Any suggestions?
Thank you!
Well, I'm not quite familiarized with IObservableList<T>, but I use Deferred Execution when working with IList<T>.
I would recommend to implement this when working with dynamic data, it will let you get the latest values whenever you need them.
check the link for more details.
You iterate through a list using a loop. Each of them works.
for is the obvious first candidate.
while works, but is a bit more writing.
foreach works - if you do not start changing the list. A quirk of foreach is that it only works with enumerators under the hood, and enumerators become invalid if the collection is changed.
One particulay thing to consider however is the "ElementAdded" Notification on (re)building the list. Usually they classes lack AddRange functions.
Unless you tell us what you do with SomeClass Instances during itteration and how the class looks, we can not tell you if it is "reactive" programming. But for me it feels like it is just a Buzzword.

does foreach loop handle Changes in list length correctly?

does foreach correctly iterate over flexible list?
for example
//will iterate over all items in list?
foreach (var obj in list)
{
//list length changes here
//ex:
list.Add(...);
list.Remove(...);
list.Concat(...);
// and so on
}
and if it does ...how?
You can't modify a collection while enumerating it inside a foreach statement.
You should use another pattern to do what you are trying to do because the for each does not allow you to change the enumerator you are looping to.
For Example:
Imagine if you run a foreach on a sorted list from the beginning, you start processing item with key="A" then you go to "B" then you change "C" to "B", what's going to happen? Your list is resorted and you don't know anymore what you are looping and where you are.
In general you "could" do it with a for(int i=dictionary.count-1; i>=0; --i) or something like that but this also depends on your context, I would really try to use another approach.
Internal Working: IEnumerator<t> is designed to enable the iterator pattern for iterating over collections of elements, rather than the length-index. IEnumerator<t> includes two members.
The first is bool MoveNext(). Using this method, we can move from one element within the collection to the next while at the same time detecting when we have enumerated through every item using the Boolean return.
The second member, a read-only property called Current, returns the element currently in process. With these two members on the collection class, it is possible to iterate over the collection simply using a while loop.
The MoveNext() method in this listing returns false when it moves past the end of the collection. This replaces the need to count elements while looping. (The last member on IEnumerator<t> , Reset(), will reset the enumeration.)
Per the documentation, if changes are made inside the loop the behavior is undefined. Undefined means that there are no restrictions on what it can do, there is no "incorrect behavior" when the behavior is undefined...crash, do what you want, send an email to your boss calling him nasty names and quiting, all equally valid. I would hope for a crash in this case, but again, whatever happens, happens and is considered "correct" according to the documentation.
You cannot change the collection inside the for each loop of the same collection.
if you want you can use for loop to change the collection length.
The collection you use in a foreach loop is immutable. As per MSDN
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects.
If you need to add or remove items from the source collection, use a
for loop.
But as per this link, it looks like this is now possible from .Net 4.0

Weird issue about IEnumerable<T> collection

I have following code:
IEnumerable<TreeItem> rootTreeItems = BuildRootTreeItems();
BuildTreeView(rootTreeItems.ElementAt(0));
private static void BuildTreeView(TreeItem treeItem)
{
TreeItem subMenuTreeItem = new TreeItem();
subMenuTreeItem.Header = "1";
TreeItem subMenuTreeItem2 = new TreeItem();
subMenuTreeItem.Header = "2";
treeItem.TreeItems.Add(subMenuTreeItem);
treeItem.TreeItems.Add(subMenuTreeItem2);
}
The weird thing is after the BuildTreeView returns, the first element of rootTreeItems doesn't have any children nodes, while it really has when debugging into the BuildTreeView method.
This problem really confused me for quite a long time, any one has any idea? thanks so much.
You're most likely hitting a deferred execution issue with IEnumerable<>. The thing to remember is that your IEnumerable<TreeItem> rootTreeItems is not a list, instead it is a promise to get a list each and every time it is asked to do so.
So, if BuildRootTreeItems() creates the IEnumerable<TreeItem> using a LINQ query and it doesn't force the execution of the query using .ToList() or .ToArray() then each time that you use rootTreeItems you are re-executing the query inside BuildRootTreeItems()
Calling rootTreeItems.ElementAt(0) will cause the query to execute. If later you try to call rootTreeItems.ElementAt(0) again then you are re-executing the query and getting back a different instance of the first TreeItem.
Try changing the first line like so:
IEnumerable<TreeItem> rootTreeItems = BuildRootTreeItems().ToArray();
This forces the execution and will prevent re-execution later. I'll bet your problem goes away.
There is possibility what your BuildRootTreeItems() method returns IEnumerable interface which elements are created via yield statement or as Gabe mentioned in comment above an implementation of IEnumerable which is created via Linq expressions chain containing Select method.
This can lead to recreating elements of IEnumerable on each access to any element from enumeration or iterating via it using Linq expressions or foreach statement.
I would go simplier:
This also could happen if
A) TreeItem is a value type(struct)
B) TreeItem.TreeItems returns a new collection
But correctness of this is difficult to deduct just from code provided.

Enumerator problem, Any way to avoid two loops?

I have a third party api, which has a class that returns an enumerator for different items in the class.
I need to remove an item in that enumerator, so I cannot use "for each". Only option I can think of is to get the count by iterating over the enum and then run a normal for loop to remove the items.
Anyone know of a way to avoid the two loops?
Thanks
[update] sorry for the confusion but Andrey below in comments is right.
Here is some pseudo code out of my head that won't work and for which I am looking a solution which won't involve two loops but I guess it's not possible:
for each (myProperty in MyProperty)
{
if (checking some criteria here)
MyProperty.Remove(myProperty)
}
MyProperty is the third party class that implements the enumerator and the remove method.
Common pattern is to do something like this:
List<Item> forDeletion = new List<Item>();
foreach (Item i in somelist)
if (condition for deletion) forDeletion.Add(i);
foreach (Item i in forDeletion)
somelist.Remove(i); //or how do you delete items
Loop through it once and create a second array which contains the items which should not be deleted.
If you know it's a collection, you can go with reverted for:
for (int i = items.Count - 1; i >= 0; i--)
{
items.RemoveAt(i);
}
Otherwise, you'll have to do two loops.
You can create something like this:
public IEnumerable<item> GetMyList()
{
foreach (var x in thirdParty )
{
if (x == ignore)
continue;
yield return x;
}
}
I need to remove an item in that enumerator
As long as this is a single item that's not a problem. The rule is that you cannot continue to iterate after modifying the collection. Thus:
foreach (var item in collection) {
if (item.Equals(toRemove) {
collection.Remove(toRemove);
break; // <== stop iterating!!
}
}
It is not possible to remove an item from an Enumerator. What you can do is to copy or filter(or both) the content of the whole enumeration sequence.
You can achieve this by using linq and do smth like this:
YourEnumerationReturningFunction().Where(item => yourRemovalCriteria);
Can you elaborate on the API and the API calls you are using?
If you receive an IEnumerator<T> or IEnumerable<T> you cannot remove any item from the sequence behind the enumerator because there is no method to do so. And you should of course not rely on down casting an received object because the implementation may change. (Actually a well designed API should not expose mutable objects holding internal state at all.)
If you receive IList<T> or something similar you can just use a normal for loop from back to front and remove the items as needed because there is no iterator which state could be corrupted. (Here the rule about exposing mutable state should apply again - modifying the returned collection should not change any state.)
IEnumerator.Count() will decide at run-time what it needs to do - enumerate to count or reflect to see it's a collection and call .Count that way.
I like SJoerd's suggestion but I worry about how many items we may be talking about.
Why not something like ..
// you don't want 2 and 3
IEnumerable<int> fromAPI = Enumerable.Range(0, 10);
IEnumerable<int> result = fromAPI.Except(new[] { 2, 3 });
A clean, readable way to do this is as follows (I'm guessing at the third-party container's API here since you haven't specified it.)
foreach(var delItem in ThirdPartyContainer.Items
.Where(item=>ShouldIDeleteThis(item))
//or: .Where(ShouldIDeleteThis)
.ToArray()) {
ThirdPartyContainer.Remove(delItem);
}
The call to .ToArray() ensures that all items to be deleted have been greedily cached before the foreach iteration begins.
Behind the scenes this involves an array and an extra iteration over that, but that's generally very cheap, and the advantage of this method over the other answers to this question is that it works on plain enumerables and does not involve tricky mutable state issues that are hard to read and easy to get wrong.
By contrast, iterating in reverse, while not rocket science, is much more prone to off-by-one errors and harder to read; and it also relies on internals of the collection such as not changing order in between deletions (e.g. better not be a binary heap, say). Manually adding items that should be deleted to a temporary list is just unnecessary code - that's what .ToArray() will do just fine :-).
an enumerator always has a private field pointing to the real collection.
you can get it via reflection.modify it.
have fun.

Changing item in foreach thru method

Let's start with the following snippet:
Foreach(Record item in RecordList){
..
item = UpdateRecord(item, 5);
..
}
The UpdateRecode function changes some field of item and returns the altered object. In this case the compiler throws an exception saying that the item can not be updated in a foreach iteration.
Now the UpdateRecord method is changed so that it returns void and the snippet would look like this:
Foreach(Record item in RecordList){
..
UpdateRecord(item, 5);
..
}
In this case the item would be updated because Record is a reference type. But it makes the code unreadable.
The project I'm working on has lots of foreach-loops with the almost the same code over and over, so I would like to create methods that update parts of the records. Is there a nice way to do this? One that make the code more readable instead of trashing it further more?
If you need to update a collection, don't use an iterator pattern, like you said, its either error prone, or smells bad.
I find that using a for loop with an index a bit clearer in this situation, as its very obvious what you are trying to do that way.
The compiler is complaining that you can't update the collection, not the record. By doing item = UpdateRecord, you are reassigning the iterator variable item.
I disagree that UpdateRecord(item, 5) is in any way unreadable - but if it makes you feel better, an extension method may make it more clear that you are changing the contents of item.
static void Update(this Record item, int value) {
// do logic
}
foreach (Record item in RecordList) {
item.Update(5);
}
Do you need to update the same list? Could you return a new (updated) enumeration instead?
foreach(Record item in RecordList){
..
yield return GetUpdatedRecord(item, 5);
..
}

Categories