Removing an item from a list c# - c#

I am having trouble with removing an item from a list in c#. My current code is below:
for (int i = current.Count - 1; i >= 0; i--)
{
foreach (ListItem li in sp_list.Items)
{
if (li.Text == current[i].uname)
{
current.RemoveAt(i);
}
}
}
The aim of the code is to compare the existing items in a list box with newly added items so I know which items have just been added. So currently I am getting the current list box items from the database (they are stored here, it is a databound list box), entering these into a list and for each of the list items, comparing them with the items in a list box and if they match, remove the item from the list.
Therefore, in the end, if I add two new entries, the list should only be storing the two newly added values.
The code does not work as the first item is removed fine, but then, the value of i is greater than current.count - and therefore I get an out of range exception!
Could someone help me with this issue? Apologies about the confusing question, it's hard to explain! Thanks

You can do it with Linq. Not sure if casting to ListItem needed (you can remove it)
current.RemoveAll(x => sp_list.Items.Cast<ListItem>()
.Any(li => li.Text == x.uname));

Once you've found the matching value, and removed it from the list, you want to break out of the inner loop to check the next item.
if (li.Text == current[i].uname)
{
current.RemoveAt(i);
break;
}

Your nesting is wrong, I think you wanted,
foreach (ListItem li in sp_list.Items)
{
for (int i = current.Count - 1; i >= 0; i--)
{
if (li.Text == current[i].uname)
{
current.RemoveAt(i);
}
}
}
alternatively, use linq,
// For lookup performance.
var items = new HashSet(sp_list.Items.Select(i => i.text));
current = current.Where(c => !items.Contains(c.uname)).ToList();

How about this:
foreach (ListItem li in sp_list.Items) {
if (current.Contains(li.Text)) {
current.Remove(li.Text);
}
}

Put a break statement after the RemoveAt so you don't remove that item again.

you can travel the list in reverse order and remove items using RemoveAt(i).
also for efficiency purposes you may want to put the ListItem texts in a Set so you can don't have to loop though the sp_list.Items for each of your current items.

Related

Trying to remove row(s) from listview

My code correctly identifies listview rows based on a textbox search criteria. However when I try to remove the unwanted row(s) I get an error message:-
InvalidArgument=Value of '-1' is not valid for 'index'.
I would be very grateful if someone could help me resolve this issue.. Thanks in advance.
foreach (ListViewItem item in listView1.Items){
foreach (ListViewItem.ListViewSubItem subItem in item.SubItems){
if (subItem.Text.ToLower().StartsWith(textBox1.Text.ToLower())){
var index = item.Index;
int.TryParse(listView1.Items[index].SubItems[4].Text, out val);
store[pos] = val;
pos++;
count++;
}else{
listView1.Items[index].Remove();
}
}
}
The problem is that you do not calculate your index in clause 'else'
}else{
listView1.Items[index].Remove();
}
You can't remove items from a collection you are iterating through Foreach, c# will throw an error because the collection changed, use for instead, and do it from the end to beginning (so you won't jump items when removing)
Try this approach:
Comments are in the code. Feel free to ask if you need additional explanation.
solution w/ Linq
using System.Linq;
...
//cast ListViewItemCollection as List<ListViewItem> for easier manipulation
var items = listView1.Items.Cast<ListViewItem>();
//first, get all items that don't have text matching with textBox text
List<int> itemIndexes =
items
.Where(item => !item.Text.ToLower().StartsWith(textBox1.Text.ToLower()))
.Select(item => item.Index).ToList();
int val;
//now, with all those indexes do your stuff
itemIndexes.ForEach(index =>
{
int.TryParse(listView1.Items[index].SubItems[4].Text, out val);
store[pos] = val;
pos++;
count++;
});
//lastly, sort indexes descending (for example: 10, 5,4,1) and remove them from listview
itemIndexes
.OrderByDescending(i=>i)
.ToList()
.ForEach(index => listView1.Items.RemoveAt(index));
EDIT: chaged code that it removes items that don't start with text box' text (like OP wanted).
EDIT 2: since you don't want to use Linq, you can solve your problem using plain old lists and loops
Main thing is, as I said in a comment, not to remove items inside of foreach loop but after you found all items that need to be removed.
Code is tested and working (without 4 lines with missing variables store, val etc ...)
solution w/o Linq
//list to hold intem indexes
List<int> itemIndexes = new List<int>();
foreach (ListViewItem item in listView1.Items)
{
foreach (ListViewItem.ListViewSubItem subItem in item.SubItems)
{
var index = item.Index;
if (subItem.Text.ToLower().StartsWith(textBox1.Text.ToLower()))
{
//your code (which doesnt compile since you didn't paste whole code)
//to test this, I had to remove following 4 lines.
int.TryParse(listView1.Items[index].SubItems[4].Text, out val);
store[pos] = val;
pos++;
count++;
}
else
{
itemIndexes.Add(index);
}
}
}
//lastly, sort indexes descending (for example: 10, 5,4,1)
//because if you remove item 1, whole list will shorten and
//you'll get index out of bounds error (in last step when
//trying to remove item with 10 but last index is 7)...
itemIndexes.Reverse();
//... and remove them from listview
foreach (int index in itemIndexes)
{
listView1.Items.RemoveAt(index);
}
tested with sample data - code will remove items that don't start with a, like bbb and dd)
ListView listView1 = new ListView();
listView1.Items.Add("aaa");
listView1.Items.Add("bbb");
listView1.Items.Add("ax");
listView1.Items.Add("dd");
TextBox textBox1 = new TextBox();
textBox1.Text = "a";

replacing an object in listview with new one?

I have a listview with few items. I am using foreach loop to check if there is a match. The code I am using looks like this:
foreach (ListViewItem test in listView1.Items)
{
if (test.SubItems[1].ToString() == item.SubItems[1].ToString())
{
test.Tag = item.Tag;
}
}
What I am trying to do is, check the 2nd index and if there is a match replace the old item 'test' with the new one 'item'.
Apparently there is no change in the listview. Is the way I am replacing the object wrong?
you can clone the item and assign directly to the list view item. but you need to change foreach loop to for loop.
for (int i = 0; i < listView1.Items.Count; i++)
{
if (listView1.Items[i].SubItems[1].ToString() == item.SubItems[1].ToString())
{
listView1.Items[i] = (ListViewItem)item.Clone();
}
}
You have updated the Tag only. You need to change test.SubItems[0], test.SubItems[1],... to see the changes.
Or you could remove old item and insert new item by using listView1.Items.Remove(...) or listView1.Items.RemoveAt(...) and listView1.Items.Insert(...). But if you need to pay account of performance you should use the first algorithm (changing test.SubItems[i]).

Safely remove list from list in loop

I am iterating through all the list in mainlist and if a specific condition occur. I want to remove that specific list.
It is not working.
for (int i = 0; i < mainList.Count; i++)
{
if (mainList[i].Dead == true) //
{
mainList.removeAt(i);//what will be affect on mainList.Count;
}
}
mainList.RemoveAll(x => x.Dead);
The way you constructed your for loop, it will only work if mainList.Count stays constant, but you are removing elements during your loop. Do you understand why that is a problem? Whenever you remove an element you end up skipping the check of the element after it.
I think you could use foreach and mainList.remove but I'm not sure. Another solution is to just make your loop count downwards:
for (int i = mainList.Count-1; i >= 0; i--)
{
if (mainList[i].Dead == true)
{
mainList.removeAt(i);
}
}
You will not be able to do that since you are altering the length of the list when you are doing so. I would do it like this instead:
var newList = mainList.Where(y => !y.Dead).ToList();
How to modify or delete items from an enumerable collection while iterating through it in C#
http://bytes.com/topic/c-sharp/answers/238241-how-do-you-modify-array-while-iterating-through

How to check if an item exists in more than one listbox? ASP.NET/C#

I have three sets of listboxes, I move items from lb1 to lb2, from lb3 to lb4 and from lb5 to lb6. The listboxes on the left contains the same items and I don't want the user to be able to submit the page if one or more items from the left listboxes is added to more than one listbox to the right. For example, item A in lb1, lb3 and lb5 can only be saved in either lb2, lb4 or lb6, not in two or three of them.
I want to perform this check before submitting the page (and later on I will add validation with javascript) and I wonder what is the most efficient way to do this.
Add all items to a list and check if there are any duplicates?
Thanks in advance.
Edit:
something like this:
List<string> groupList = new List<string>();
foreach (ListItem item in lbFullAccess.Items)
{
groupList.Add(item.Value.ToString());
}
foreach (ListItem item in lbContributor.Items)
{
groupList.Add(item.Value.ToString());
}
foreach (ListItem item in lblReadOnly.Items)
{
groupList.Add(item.Value.ToString());
}
Well, there's a hundred different ways you could do it. Absolutely nothing wrong with your suggestion of iteration.
You could have a little fun with LINQ:
public bool AreAllValuesUnique()
{
// Build up a linq expression of all of the ListItems
// by concatenating each sequence
var allItems = lbFullAccess.Items.Cast<ListItem>()
.Concat(lbContributor.Items.Cast<ListItem>())
.Concat(lbReadOnly.Items.Cast<ListItem>());
// Group the previous linq expression by value (so they will be in groups of "A", "B", etc)
var groupedByValue = allItems.GroupBy(i => i.Value);
// Finally, return that all groups must have a count of only one element
// So each value can only appear once
return groupedByValue.All(g => g.Count() == 1);
}
Not really sure about the performance of calling Cast (converting each element of the ListItemCollection to a ListItem, resulting in an IEnumerable) on each collection, but it is probably negligible.

Foreach doesn't go through all items?

I have this code:
foreach (var item in ListView1.Items)
{
ListView1.Items.Remove(item);
ListView21.Items.Add(item);
}
the loop stops at half of the items?
Any idea?
EDIT
Well, maybe it's my mistake, I need to clarify that this is UltraListView control from Infrajistics, where I can't add item to another list unless I remove it or clone it from the original list.
But thanks, most of the comments regarding do not modify the list within the loop were correct, so this code works:
foreach (var item in listView1.Items)
{
var i = item.Clone(true);
listView2.Items.Add(i);
}
listView1.Items.Clear();
Thanks,
You cannot modify iterated collection, it should die with exception (or in undefined behavior).
Try making a copy of the array:
foreach (var item in ListView1.Items.ToArray())
{
ListView1.Items.Remove(item);
ListView21.Items.Add(item);
}
EDIT:
in fact, your example code can be achieved by writing:
ListView21.Items.AddRange(ListView1.Items);
ListView1.Items.Clear();
(which in fact isn't EXACTLY what you are doing, but gives the same result and I think it won't bother you having the same content in both listviews for a moment). The latter is supported since .NET2.0, first solution requires linq, and therefore .NET3.5.
You are modifying the collection you are looping through. Try using a for statement from top to bottom (from the item with the highest index to 0).
for (int i = ListView1.Items.Count - 1; i >= 0; i--)
{
var item = ListView1.Items[i];
ListView1.Items.Remove(item);
ListView21.Items.Insert(0, item);
}
It will cause a runtime exception, complaining that you cannot modify the collection while iterating through it. You have to use for loop instead.
for(int index = Items.Count; index > 0; index--)
{
.......
// use Add and RemoveAt
}
EDIT : As mentioned by others. If you just need to move items from one collection to the other. AddRange and Clear will be better.
Do you get any exception or error message? Looping in a collection and remove items from the same collection is always a bad idea.
This looks like the WinForms list view control, so:
ListViewItem[] items = ListView1.Items.ToArray();
ListView1.Items.Clear();
ListView21.Items.AddRange(items);
Why just not CopyTo() to new list and then Clear() items?
You are looping through all items, removing all of them, then adding them to another list. As others have commented, you cannot remove items from a list in a for-each. Why not looping through all items to add them to your other list, and then remove them all in one go?
foreach (var item in ListView1.Items)
{
ListView21.Items.Add(item);
}
ListView1.Items.Clear(); // remove all
PS: is this an ASP.NET listview or a WinForms listview?
That's because you're changing the collection inside the loop.
Use a normal for loop as follows:
for(int i=0; i < ListView1.Items.Count-1; i++)
{
ListView21.Items.Add(ListView1.Items[i]);
ListView1.Items.RemoveAt(i);
}

Categories

Resources