Solved

Remove items from a list

Posted on 2013-05-29
18
313 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
PeopleSoft Has Never Been Easier

PeopleSoft Adoption Made Smooth & Simple!

On-The-Job Training Is made Intuitive & Easy With WalkMe's On-Screen Guidance Tool.  Claim Your Free WalkMe Account Now

 
LVL 75

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 75

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 75

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
 
LVL 75

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 75

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 75

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

Guide to Performance: Optimization & Monitoring

Nowadays, monitoring is a mixture of tools, systems, and codes—making it a very complex process. And with this complexity, comes variables for failure. Get DZone’s new Guide to Performance to learn how to proactively find these variables and solve them before a disruption occurs.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

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…
For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an antispam), the admini…
Are you ready to implement Active Directory best practices without reading 300+ pages? You're in luck. In this webinar hosted by Skyport Systems, you gain insight into Microsoft's latest comprehensive guide, with tips on the best and easiest way…

732 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