C# for loop skipping last item - c#

I have the below for loop
int listCount = _itemCollection.Count;
//_itemCollection is of type SPListItemCollection
for (int i=0;i<listCount;i++)
{
var item = _itemCollection[i]; // just to prevent changes in all places inside the for loop
if(item['expirydate']>today){
item.delete();
listCount--; //as I am removing 1 item, I am decrementing count
}
}
In this for loop, I am iterating through the items in itemcollection and deleting some of them. i.e item will be removed from itemcollection array and so itemcollection.count will be reduced by 1
This is not deleting the 3rd item every time, when I have 3 items to delete
I am not sure what condition should be used for getting it right

You should go in the reverse order as below and use for instead of foreach as below.
int listCount = _itemCollection.Count;
for (int i = listCount - 1; i >= 0; i--)
{
var item = _itemCollection[i]; // just to prevent changes in all places inside the for loop
if(item['expirydate'] > today){
item.delete();
}
}

You can do something like this:
_itemCollection.RemoveAll(item => item['expirydate'] > today);
This removes all the items that matches the given condition.
To remove item from SPListItemCollection check this documentation

Try this:
int listCount = _itemCollection.Count;
for (int i = 0; i < listCount; i++)
{
var item = _itemCollection[i];
if(item [expirydate] > today)
{
_itemCollection.Remove(item);
listCount--;
}
}
This may fulfill your want. Here you can directly use _itemCollection[i] instead of item.
I hope this may help you. Enjoy coding.

Related

Way to delete row from list if criteria met

i am running two lists through a fuzzy matching program and want to be able to remove that row from the list if a match occurs but am having problems doing so. here is my code
foreach (var name in list)
{
foreach (var stepone in Step1)
{
if (FuzzyMatching(name.Full(), stepone.Full()) >= 90)
{
csvcontent.AppendLine(name.Full());
//find a way to delete list record out of list
//list.Remove(name);
}
}
}
you can mark in another list all the items to delete.
When the loop is finished you can delete all the items in the new created list from that list.
With LINQ is simply:
list1 = list1.Where(x => !itemToDeleteList.Contains(x)).ToList();
You can't remove an item from a list using a ForEach loop. In this case you should use a for loop.
var list = new List<string>();
list.Add("a");
list.Add("b");
list.Add("c");
list.Add("d");
for(int i = 0; i < list.Count(); i++)
{
if(list[i].Contains("a"))
{
list.RemoveAt(i);
}
}
Your results list will be:
b
c
d
Leaving the above for reference, but to #LarsTech comment below, list.RemoveAt breaks the loop so it will only remove 1 item. If you are wanting to remove multiple items, Alex's other answer with the Linq would be the cleanest way to remove it.
If you don't want to create a second list to house the items to be deleted, you can also do the below:
for (int i = 0; i < list.Count(); i++)
{
if (list[i].Contains("a"))
{
list = list.Where(x => !x.Contains("a")).ToList();
}
}
When you loop over a list with foreach and try to remove an item, you will get a runtime InvalidOperationException:
Collection was modified, enumeration operation may not execute
So you can use a for loop, which can handle modifications to the list. However, when you delete an item, all higher indexes will shift down. This will mean you skip an item.
The solution is to iterate in reverse order:
var list = new List<string>();
list.Add("a");
list.Add("b");
list.Add("b");
list.Add("c");
list.Add("d");
for (int i = list.Count()-1; i>=0; i--)
{
if (list[i].Contains("b"))
{
list.RemoveAt(i);
}
}
// result: a, c, d
you have to start at "count - 1", because that is the highest actual index
the last index to use is 0 (the start of the list)
and decrease the index after every iteration

Change List inside a for loop

I'm trying to modify a list inside a for value
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
theList.remove(component);
else
theList.add(component);
}
I get an ArgumentOutOfRangeException with this method.
Is there any method to accomplish this?
It can be solved by iterating backwards and using indexes instead of items:
for (int i = list.Count - 1; i > 0; i--)
{
if(condition)
list.RemoveAt(i);
else
list.Add(component);
}
Some explanation: when you iterating over collection you shouldn't change items in the scope. Iterators will detect that and throw (and in case of foreach you must use copy of list). But in case of using indexes (RemoveAt() method) and when iterating backward you are safe as for next iteration the scope doesn't include deleted items. Add() is adding to the end, therefore new item is never in scope.
I'll add few more solutions, which one is better decide yourself:
Classical foreach with copy first:
foreach(var item in list.ToArray()) // imho `ToArray` is better than `ToList`
if(condition)
list.Remove(item);
else
list.Add(component);
New list as result:
var result = new List<...>();
foreach(var item in list)
result.Add(condition ? component : item); // not sure here, but should give you idea
list = result;
This is also a bad practice to mutate the list while iterating over it.
This is an alternative:
theList.RemoveAll(someCircunstances);
you are getting an out of range exception because indexes start on 0.
as stated above, one solution is to remove 1 from theList.count, and another solution is to initiate i at 1 instead of 0.
think of this: if your list has 1 element in it, the index of that element is 0, if you have 100 elements, the index of your hundreth element is 99.
you are thinking of the list like: [1][2][3], while it's actually [0][1][2]
The problem here is that you are deleting values out of the list and then you iterate throught it again with an index which is already removed -> ArgumentOutOfRangeException
So to solve this i suggest you to split it up to two for loops:
for (int i = theList.Count - 1; i >= 0; i--) {
if(someCircunstances)
theList.remove(component);
}
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
theList.add(component);
}
I am agree with Tamas, that don't mutate the list while iterating , there is another way to achieve your point
List<someType> ToRemove=new List<someType>() ; //some type is same type as theList is
List<someType> ToAdd=new List<someType>();
for (int i = 0; i < theList.Count; i++) {
if(someCircunstances)
ToRemove.add(component);
else
ToAdd.add(component);
}
theList=((theList.Except(ToRemove)).Concat(ToAdd)).ToList();
Based on the comments, you need to be able to apply the same logic for newly created items.
You need to do something like this:
public void DoIt(List<MyObject> theList)
{
List<MyObject> items_to_remove = new List<MyObject>();
List<MyObject> items_to_add = new List<MyObject>();
for (int i = 0; i < theList.Count; i++)
{
if (someCircunstances)
items_to_remove.Add(....); //Remove some existing item
else
items_to_add.Add(....); //Add a new item
}
if(items_to_remove.Count > 0)
items_to_remove.ForEach(x => theList.Remove(x));
if (items_to_add.Count > 0)
{
DoIt(items_to_add); //Recursively process new objects
theList.AddRange(items_to_add);
}
}
The idea is that you insert the items to add and the items to remove in their own lists.
Then after the iteration, you remove the items that need to be removed.
After that you need to add the items to add. However, before doing that you need to run the same logic on them, and that is the explanation for the recursive call.
Please note that I am using MyObject because I don't know the type of your list. Use whatever type that you are working with.
If you can use the current index of the loop to remove the item from the lst, you can do this easily like so:
using System;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var numbers = Enumerable.Range(1, 20).ToList();
var rng = new Random();
for (int i = 0; i < numbers.Count; ++i)
{
if (rng.NextDouble() >= 0.5) // If "someCircumstances"
{
numbers.Add(numbers[i]*2);
}
else
{
// Assume here you have some way to determine the
// index of the item to remove.
// For demo purposes, I'll just calculate a random index.
int index = rng.Next(0, numbers.Count);
if (index >= i)
--i;
numbers.RemoveAt(index);
}
}
Console.WriteLine(string.Join("\n", numbers));
}
}
}
This will also loop over all the numbers added to the end of the list. The value of numbers.Count is recomputed at each iteration, so when it changes, the loop will be extended appropriately.
(Offtopic) BONUS QUESTION: In the above code, what will be the average size of the list when the loop exits? And what would be the maximum size?

How to remove more items from combobox?

I am trying to remove more items from a combobox but the application is only removing one item at a time.
The combobox has a list of email addresses. I want to remove empty items (""), and those that don't have # inside of text.
Code below only removes one item at a time.
for (int i = 0; i < cmbTo.Items.Count; i++)
{
string st = cmbTo.Items[i].ToString();
if (st == "" || st.IndexOf("#") == -1)
{
cmbTo.Items.RemoveAt(i);
}
}
How can I rewrite this?
Hint: Think about what happens to the i variable when you remove an item
...
When you RemoveAt an item, the item is removed, and every subsequent item moves up one index. Your loop then hits the bottom, where it goes back to the top, increments i, and moves on.
Result? You just skipped an item. If this is the last item in the list, then the loop exists.
Instead, manually decrement i to offset your removal, so that everything works:
for (int i = 0; i < cmbTo.Items.Count; i++)
{
string st = cmbTo.Items[i].ToString();
if (st == "" || st.IndexOf("#") == -1)
{
cmbTo.Items.RemoveAt(i);
i--;
}
}
Your code doesn't work because the moment you remove an item from the collection, the Count() decreases and the for loop exits before going through all the list of items.
You need to first create a list of elements to remove (put them in a temp list) and then iterate through the newly created list calling cmbTo.Items.Remove(currentElement);
When you remove an item from a combobox, the indices of the following items change and your item count will change. Could that account for the behavior you're seeing?
Just do the removal in the opposite direction (i.e. from the end to the front), and you won't need to worry about adjusting i1 when the item is removed:
var items = cmbTo.Items;
int i = items.Count;
while (i > 0) {
--i;
string st = items[i].ToString();
if (st == "" || st.IndexOf("#") < 0)
items.RemoveAt(i);
}
1 Which you currently don't do, so some items that should potentially be removed are skipped, which causes your problem.

Help with converting foreach loop to while loop in c#

I had learnt by reading your great answers here, that it is not good practice deleting items from within a foreach loop, as it is (and I quote) "Sawing off the branch you're sitting on".
My code currently removes the text from the dropdownlist, but the actual item remains (just without text displayed).
In other words, it isn't deleting, and probably can't because you can't delete from within a foreach loop.
After hours of trying I am unable to get my head around a way of doing it.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
//And deletes the item
menuItem.DropDownItems.Remove(mItem);
}
}
}
}
But it isn't deleting because it is within a foreach loop!
I would greatly appreciate your help, and be truly amazed if anyone can get their head around this code :)
Kind Regards
Fun with LINQ!
// Loop through the checked items, same as you did.
foreach (var checkedItem in this.organizeFav.CheckedItems)
{
// Cast from IEnumerable to IEnumerable<T> so we can abuse LINQ
var matches = favoritesToolStripMenuItem.DropDownItems.Cast<ToolStripItem>()
// Only items that the Text match
.Where(item => item.Text == checkedItem.Text)
// Don't match separators
.Where(item => item is ToolStripMenuItem)
// Select the keys for the later .Remove call
.Select(item => item.Name);
// Loop through all matches
foreach (var key in matches)
{
// Remove them with the Remove(string key) overload.
favoritesToolStripMenuItem.Remove(key);
}
}
You don't need a foreach loop - just use a regular loop but go in reverse, start at the end and go to the beginning.
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
//this *replaces* the foreach loop
for(int j = favoritesToolStripMenuItem.DropDownItems.Count - 1; j >= 0; j--)
{
ToolStripMenuItem menuItem = favoritesToolStripMenuItem.DropDownItems[j] as ToolStripMenuItem;
//This rules out seperators
if (menuItem != null)
{
//This matches the dropdownitems text to the CheckedItems String
if (menuItem.Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
favoritesToolStripMenuItem.DropDownItems.Remove(menuItem);
}
}
}
}
this was #Kurresmack's code rearranged, i just coded it directly here in the page so excuse any small syntax error or anything obvious i overlooked (disclaimer: it is a sample!!)
You can still treat favoritesToolStripMenuItem.DropDownItems as a collection like you were, but you don't have to enumerate over it using a foreach. This cuts down on a few lines of code, and it works because you are iterating it in reverse order, you will not get an index out of bounds exception.
Try something like this:
//For each checked box, run the delete code
for (int i = 0; i < this.organizeFav.CheckedItems.Count; i++)
{
List<ToolStripItem> toRemove = new List<ToolStripItem>();
//this is the foreach loop
foreach (ToolStripItem mItem in favoritesToolStripMenuItem.DropDownItems)
{
//This rules out seperators
if (mItem is ToolStripMenuItem)
{
ToolStripMenuItem menuItem = mItem as ToolStripMenuItem;
//This matches the dropdownitems text to the CheckedItems String
if (((ToolStripMenuItem)mItem).Text.ToString() == organizeFav.CheckedItems[i].ToString())
{
toRemove.Add(mItem);
}
}
}
foreach(var item in toRemove)
{
favoritesToolStripMenuItem.DropDownItems.Remove(item);
}
}
To my mind, the way to make the code work is:
1. Create an instance of the type the favoritesToolStripMenuItem.DropDownItems collection is.
2. In the foreach loop, add all items, you do not want to be removed, to that collection.
3. Make favoritesToolStripMenuItem.DropDownItems to point to the new collection. Or clear favoritesToolStripMenuItem.DropDownItems and load the items from the new collection to it.
Hope this helps
Instead of a foreach use a reverse for-Loop:
for(int reverseIndex = myList.Count - 1; reverseIndex >= 0; reverseIndex--)
{
var currentItem = myList[reverseIndex];
if(MatchMyCondition(currentItem))
{
myList.Remove(currentItem);
}
}

How to modify or delete items from an enumerable collection while iterating through it in C#

I have to delete some rows from a data table. I've heard that it is not ok to change a collection while iterating through it. So instead of a for loop in which I check if a row meets the demands for deletion and then mark it as deleted, I should first iterate through the data table and add all of the rows in a list, then iterate through the list and mark the rows for deletions. What are the reasons for this, and what alternatives do I have (instead of using the rows list I mean)?.
Iterating Backwards through the List sounds like a better approach, because if you remove an element and other elements "fall into the gap", that does not matter because you have already looked at those. Also, you do not have to worry about your counter variable becoming larger than the .Count.
List<int> test = new List<int>();
test.Add(1);
test.Add(2);
test.Add(3);
test.Add(4);
test.Add(5);
test.Add(6);
test.Add(7);
test.Add(8);
for (int i = test.Count-1; i > -1; i--)
{
if(someCondition){
test.RemoveAt(i);
}
}
Taking #bruno code, I'd do it backwards.
Because when you move backwards, the missing array indices do not interfere with the order of your loop.
var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
for (int i = l.Count - 1; i >= 0; i--)
if (l[i] % 2 == 0)
l.RemoveAt(i);
foreach (var i in l)
{
Console.WriteLine(i);
}
But seriuosly, these days, I'd use LINQ:
var l = new List<int>(new int[] { 0, 1, 2, 3, 4, 5, 6 });
l.RemoveAll(n => n % 2 == 0);
You can remove elements from a collection if you use a simple for loop.
Take a look at this example:
var l = new List<int>();
l.Add(0);
l.Add(1);
l.Add(2);
l.Add(3);
l.Add(4);
l.Add(5);
l.Add(6);
for (int i = 0; i < l.Count; i++)
{
if (l[i] % 2 == 0)
{
l.RemoveAt(i);
i--;
}
}
foreach (var i in l)
{
Console.WriteLine(i);
}
Since you're working with a DataTable and need to be able to persist any changes back to the server with a table adapter (see comments), here is an example of how you should delete rows:
DataTable dt;
// remove all rows where the last name starts with "B"
foreach (DataRow row in dt.Rows)
{
if (row["LASTNAME"].ToString().StartsWith("B"))
{
// mark the row for deletion:
row.Delete();
}
}
Calling delete on the rows will change their RowState property to Deleted, but leave the deleted rows in the table. If you still need to work with this table before persisting changes back to the server (like if you want to display the table's contents minus the deleted rows), you need to check the RowState of each row as you're iterating through it like this:
foreach (DataRow row in dt.Rows)
{
if (row.RowState != DataRowState.Deleted)
{
// this row has not been deleted - go ahead and show it
}
}
Removing rows from the collection (as in bruno's answer) will break the table adapter, and should generally not be done with a DataTable.
A while loop would handle this:
int i = 0;
while(i < list.Count)
{
if(<codition for removing element met>)
{
list.RemoveAt(i);
}
else
{
i++;
}
}
chakrit's solution can also be used if you are targetting .NET 2.0 (no LINQ/lambda expressions) by using a delegate rather than a lambda expression:
public bool IsMatch(int item) {
return (item % 3 == 1); // put whatever condition you want here
}
public void RemoveMatching() {
List<int> x = new List<int>();
x.RemoveAll(new Predicate<int>(IsMatch));
}
Deleting or adding to the list whilst iterating through it can break it, like you said.
I often used a two list approach to solve the problem:
ArrayList matches = new ArrayList(); //second list
for MyObject obj in my_list
{
if (obj.property == value_i_care_about)
matches.addLast(obj);
}
//now modify
for MyObject m in matches
{
my_list.remove(m); //use second list to delete from first list
}
//finished.
When I need to remove an item from a collection that I am enumerating I usually enumerate it in reverse.

Categories