2

I have a List of string values and and some of the values contains xxx or XXX in front.

xxxRed
xxxYellow
xxxxCareful with that axe Eugene!
xxxxxxdedicum aceasta frumoasa Melodia
xxxxLeaders
xxxxWorking Around - titles
XXXXXNothing To Fear
xxxxAvoiding standards
xxxFirst Aid

List<string> lstTitles = new List<string>();

This is what I have tried

for (int i=0; i < lstTitles.Count; i++)
            {
                string title = lstTitles[i].ToLower().Trim();
                if (title[0] == 'x')
                {
                    lstTitles.Remove(lstTitles[i]);

                }
            }

Problem I have is that only some of the values are removed but not all of them.

Is there perhaps a better way of removing these values?

6 Answers 6

10

Use RemoveAll method

lstTitles.RemoveAll(s => s[0] == 'x' || s[0] == 'X');

and you may want to use StartsWith instead of comparing first char.

lstTitles.RemoveAll(s => s.StartsWith("x",StringComparison.InvariantCultureIgnoreCase));
Sign up to request clarification or add additional context in comments.

3 Comments

|| s[0] == 'X'... also, would fail if a string item is null or empty.
Does not handle the case, and OP mentionned wants to remove items starting with 'xxx', not 'x' ;)
@l4V, actually, OP's code is wrong, because, it would remove 'Xavier' from the list. And i'm sure he doesn't want that
3

Problem I have is that Only some of the values are removed but not all of them.

Because you're skipping items. When you call Remove(), the next item will be at index i, but you'll increase i in the next loop.

It can be solved by iterating over a copy of the list, and removing unwanted items in the original:

foreach (var item in lstTitles.ToList())
{
    if (item.StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
    {
        lstTitles.Remove(item);
    }
}

Though this involves creating a copy of the list, which isn't really useful, as well as calling Remove() which itself is far from performant.

So you could invert your for-loop, to remove the last items first which doesn't change the indexing for unprocessed items:

for (int i = lstTitles.Count - 1; i > 0; i--)
{
    if (lstTitles[i].StartsWith("x", StringComparison.InvariantCultureIgnoreCase))
    {
        lstTitles.RemoveAt(i);
    }
}

But as @I4V points out, all of this logic already is in List<T>.RemoveAll(), which is nicer to read and probably optimized for some edge cases, so there's little use to hand-code it again.

7 Comments

This will throw an error because the collection has been modified while in a foreach loop.
@Sean no it doesn't, because I call .ToList().
Oh, sorry, missed that, my bad =]
So in every iteration you're creating a new collection from the old. That's really inefficient.
@TimSchmelter that is not how foreach() works.
|
2

That's because your skipping values.

Suppose your list contains ['xVal1', 'xVal2', 'val3', 'xVal4', 'val5']. At first your i is 0, and you look at list[0], which is 'xVal1', so you remove it.

Now your list contains ['xVal2', 'val3', 'xVal4', 'val5'], and your i is 1. So you look at list[1] which is 'val3'. You ignored xVal2 !

You can start at the back of the list and go to the front, although you will still have a potential bug in case there are identical values you remove.

A shorter way would be to use LINQ:

var newList = lstTitles.Where(title=>!title.StartsWith('xxx'))

Comments

2

Instead of ToLower you should use the overload of StartsWith which allows to pass a StringComparison.OrdinalIgnoreCase.

Then use List.RemoveAll which is the most readable, most efficient and shortest approach:

lstTitles.RemoveAll(s => s.TrimStart().StartsWith("x", StringComparison.OrdinalIgnoreCase));

Demo

Comments

2

I think, you'd better just create a new list this way

list = list
    .Where(i => ! i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase))
    .ToList();

It would have a O(n) complexity whereas, trying to remove then 1 by 1 would be in O(n^2).

2 Comments

Why would looping over a list once have a complexity of > O(n)?
I was refering to @CodeCaster's code which uses list.Remove(string) which is by it self in O(n). So if you loop through the list, and for some items, you use list.Remove(string), you could have a combined complexity of O(n^2).
1

This could work also :

list.RemoveAll(i => i.StartsWith("xxx", StringComparison.InvariantCultureIgnoreCase));

Handles all cases and without a second list.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.