Solved

Remove items from a list

Posted on 2013-05-29
18
307 Views
Last Modified: 2013-06-05
Ok guys I have a list
private List<String> _OptionCompare = new List<string>();

Open in new window


I am using a button click to populate the list...
 _OptionCompare.Add("RFQ Number");
            _OptionCompare.Add("ManPartNo");
            _OptionCompare.Add("Manufacturer");
            _OptionCompare.Add("CustPartNo");

Open in new window


I then have another button click that iterates the loop (removing from the loop the value of gridControl1.Columns[3].Tag.ToString() if it exists.
foreach (string summin in _OptionCompare)
            {
                System.Windows.MessageBox.Show(summin.ToString());

                while (_OptionCompare.Contains(gridControl1.Columns[3].Tag.ToString()))
                {
                    _OptionCompare.Remove(gridControl1.Columns[3].Tag.ToString());
                }

            }

Open in new window


This works if gridControl1.Columns[3].Tag.ToString() contains nothing in the list. However if it does then I get the error... Collection was modified; enumeration operation may not execute. on the for loop.

Any ideas?

Thanks,
Dean
0
Comment
Question by:deanlee17
  • 6
  • 6
  • 5
  • +1
18 Comments
 
LVL 9

Expert Comment

by:TvMpt
ID: 39204087
Have a look at the RemoveAll method of List<T>. It lets you remove an item based on a predicate.
0
 
LVL 42

Assisted Solution

by:sedgwick
sedgwick earned 167 total points
ID: 39204090
u can't amend the actual enumerator while u loop through it, u need to output the results  to another structure:
List<string> result = new List<string>();
string  str = gridControl1.Columns[3].Tag.ToString();
foreach (string summin in _OptionCompare)
            {
if(summin != str){
result.Add(summin);
}
}

//update the list
_OptionCompare = result;

Open in new window


u can do it in linq in one liner:
_OptionCompare= _OptionCompare.Where(str=>(str!=gridControl1.Columns[3].Tag.ToString())).ToList();

Open in new window

0
 

Author Comment

by:deanlee17
ID: 39204114
Sedgwick, that would make perfect sense.
0
 
LVL 74

Assisted Solution

by:käµfm³d 👽
käµfm³d   👽 earned 333 total points
ID: 39204210
Why do you need the foreach at all? Your while loop alone should suffice in removing the items from the list.

You can switch to a reverse for loop: to remove items from the list:

e.g.

for(int i = _OptionCompare.Length - 1; i > -1; i--)
{
	string summin = _OptionCompare[i];
	
	System.Windows.MessageBox.Show(summin.ToString());

	if (summin == gridControl1.Columns[3].Tag.ToString()))
	{
	    _OptionCompare.RemoveAt(i);
	}
}

Open in new window

0
 

Author Comment

by:deanlee17
ID: 39204277
Kaufmed,

Why does for each work in reverse?

Ah so the while loop will iterate through the list anyway?
0
 
LVL 42

Expert Comment

by:sedgwick
ID: 39204304
foreach uses readonly enumerator while for and index is not readonly.
that's why no exception is called when call Remove inside For scope, as oppose to foreach scope.
0
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39204312
Because a for is not using an iterator. With a for you are indexing the list each time you want to access an item. The reason you go backwards is because as you remove items from the list, obviously the length of the list is going to decrease. A for loop's variable is initialized before the loop starts. If you go forward, you would (typically) initialize the variable to the length of the list. This only happens once. So if you start with 5 items in the list, and you remove the first 2, by the time you get to the 4th item, there are only 3 items left in the list. Your variable is now at "i = 4" (for example), but your list has a maximum index of 3. You will encounter an IndexOutOfRangeException.

Looping the list in reverse avoids encountering an exception because each time you remove a single item, the next iteration of the loop updates the loop variable with one less than the previous iteration (based on how you structure the loop). Each time the loop iterates, the loop variable will always point to a valid index within the list--provided you don't remove two items during any single iteration of the loop; then you would have problems.
0
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39204320
that's why no exception is called when call Remove inside For scope
No, that's not why. I could easily generate an exception (not the same exact exception, but an exception nonetheless) by iterating the loop forwards, as I described above.
0
 
LVL 42

Expert Comment

by:sedgwick
ID: 39204327
iterating backwards will yield no exception, that's why your example worked.
as oppose to loop forward, which yields exception when remove.
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39204332
Ah so the while loop will iterate through the list anyway?
Given the usage in this discussion, "iterate" might not be the best term to use when discussing a while loop, but yes, it will. It's going to be less performant than a foreach or for loop since there is the potential for the while, based on how you've written it, to loop over the list multiple times.
0
 

Author Comment

by:deanlee17
ID: 39204364
Ok guys you have both given excellent feedback. So assuming each of you would have come across this how would you code it? in terms of clean code and performance.

The reason I am doing this is I have a row of dropdown boxes (containing 10 options) at the top of a datagrid, before an insert is done to the database I want to ensure that 4 items in the dropdowns have been allocated and obv add them back to the list if the combo is reset.

I hope that makes sense?
0
 
LVL 74

Expert Comment

by:käµfm³d 👽
ID: 39204392
I'm a fan of LINQ for things like this--see sedgwick's earlier suggestion ( http:#a39204090 ). If you run into performance issues--not likely--then you can switch to a for approach.

I need to correct a statement I made earlier:
If you go forward, you would (typically) initialize the variable to the length of the list.
Going forwards, you wouldn't initialize the variable to the length of the list; you would do this when going backwards. In the forwards approach you would initialize the variable to zero--the first index of the list--and you would set the boundary condition to be that the variable is less than the length of the list.
0
 

Author Comment

by:deanlee17
ID: 39204409
Ah ok, are you a fan of Linq because its a simple 1 liner? Im assuming the difference in performance is minimal?
0
 
LVL 42

Expert Comment

by:sedgwick
ID: 39204428
linq has other points that needs to take under consideration, specially when dealing with bug data structures.
in your case i'd go with linq cause its quick and neat.
0
 

Author Comment

by:deanlee17
ID: 39204484
Ok and whats the most efficient way (with Linq) to add an item into the list if it doesn't currently exist? ie when the combo box is reset?
0
 
LVL 42

Expert Comment

by:sedgwick
ID: 39204525
to check if item is exists in list has a known complexity, for sorted and nonsorted lists.
so linq doesn't really assists you in this case.
besides List has a method called Contains which uses default equality comparer of the type, to check if item already exists in list.
List<T>.Contains Method
0
 
LVL 74

Accepted Solution

by:
käµfm³d   👽 earned 333 total points
ID: 39204580
Ah ok, are you a fan of Linq because its a simple 1 liner? Im assuming the difference in performance is minimal?
Yes. Don't get me wrong, you can easily write complicated and obscure LINQ statments with ease. Nevertheless, LINQ statements can be written to effectively and concisely convey what it is they are doing.

LINQ is inherently slower based on what it does under the hood, but more often than not you won't notice the difference.

Ok and whats the most efficient way (with Linq) to add an item into the list if it doesn't currently exist? ie when the combo box is reset?

If you've got the same 4 options being repopulated within the CB, then I'd probably just maintain a class-level array that you can quickly pop into the list.

e.g.

private readonly string[] DEFAULT_CB_OPTIONS = { "RFQ Number", "ManPartNo", "Manufacturer", "CustPartNo" };

...


// Set defaults once cleared
_OptionCompare.AddRange(DEFAULT_CB_OPTIONS);

Open in new window


I don't see how you'll escape having to add each item back to the list once it's cleared--i.e. you're always going to have to call some form of Add*.
0
 

Author Comment

by:deanlee17
ID: 39204627
@kaufmed, Your example could be right and that all the combos could be reset at once if a new dataset is added to the datagrid. However sometimes a single CB could have its value reset , so this is when id like to check whats currently in the list and add the item back in (if its one of the 4 required options).

Im beginning to think I may have gone about this the wrong way.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Windows Service with UDP 2 28
Word Template Mail merge with vb.net 4 40
ConsoleSql 1 22
Events in static methods 3 31
Welcome my friends to the second instalment and follow-up to our Minify and Concatenate Your Scripts and Stylesheets (http://www.experts-exchange.com/Programming/Languages/.NET/ASP.NET/A_4334-Minify-and-Concatenate-Your-Scripts-and-Stylesheets.html)…
Introduction Hi all and welcome to my first article on Experts Exchange. A while ago, someone asked me if i could do some tutorials on object oriented programming. I decided to do them on C#. Now you may ask me, why's that? Well, one of the re…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, Just open a new email message.  In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now