13

I have a list from the .NET collections library and I want to remove a single element. Sadly, I cannot find it by comparing directly with another object.

I fear that using FindIndex and RemoveAt will cause multiple traversals of the list.

I don't know how to use Enumerators to remove elements, otherwise that could have worked.

RemoveAll does what I need, but will not stop after one element is found.

Ideas?

Steinbitglis
  • 2,482
  • 2
  • 27
  • 40
  • Could you show some example code? – Wouter de Kort Jan 26 '12 at 16:07
  • The questions is tagged `linked-list` but the description suggests `List`. Which is it? – Ani Jan 26 '12 at 16:08
  • I thought List was a kind of linked list? Is it not? – Steinbitglis Jan 26 '12 at 16:09
  • 1
    @Steinbitglis: `List ` is not a linked-list; it's a [dynamic array](http://en.wikipedia.org/wiki/Dynamic_array). – Ani Jan 26 '12 at 16:10
  • 1
    @Steinbitglis: No, it's not. It's array-backed. – Jon Skeet Jan 26 '12 at 16:11
  • To clarify - you want to remove the _first_ match, even if there are multiple? – Oded Jan 26 '12 at 16:11
  • Do you really need to use a List? It seems to me that an HashSet or a Dictionary would be better suited in this case (with O(1) constant-time add, remove and find operations). –  Jan 26 '12 at 16:26
  • I think actually that the constant factors of HashSet and Dictionary would blow this out of proportions. But I'm looking into LinkedList, since that's what I thought I had in the first place. – Steinbitglis Jan 26 '12 at 16:29

4 Answers4

17

List<T> has a FindIndex method that accepts a predicate

int index = words.FindIndex(s => s.StartsWith("x"));
if (index >= 0)
{
    words.RemoveAt(index);
}

Removes first word starting with "x". words is assumed to be a List<string> in this example.

kirgod
  • 189
  • 3
  • 10
Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • If list has a constant-time index lookup, I think this will be ok. I was worried that RemoveAt would also traverse the list. – Steinbitglis Jan 26 '12 at 16:14
  • 1
    @Steinbitglis: It's an O(n) operation, because it has to copy everything. Did you actually *want* a linked list? – Jon Skeet Jan 26 '12 at 16:16
  • Well, I have very few elements, that keep coming and going. I think at least hashtable would be stupid. I don't see any problems with linked lists, other than this seemingly trivial optimization that I can't figure out. – Steinbitglis Jan 26 '12 at 16:19
  • I switched to LinkedList and I hope I will get Deque-like operations, where I mostly check 1 or 2 elements. – Steinbitglis Jan 26 '12 at 16:51
  • @Steinbitglis: If you want LinkedList, I'll undelete my answer, as it already contains a solution for you... – Jon Skeet Jan 26 '12 at 17:22
  • In order to find an item based on a predicate, both list types require a list traversal, which is an O(n) operation. Removing an item from a `List` at a specific index is O(n) because items have to be moved. Removing an item from a `LinkedList` at the current node is an O(1) operation. *(Replaces an old comment that I removed).* – Olivier Jacot-Descombes Dec 24 '13 at 15:54
2

If you want to remove only the first element that matches a predicate you can use the following (example):

List<int> list = new List<int>();
list.Remove(list.FirstOrDefault(x => x = 10));

where (x => x = 10) is obviously your predicate for matching the objects.

Strillo
  • 2,952
  • 13
  • 15
1

EDIT: Now the OP has changed to use a LinkedList<T>, it's easy to give an answer which only iterates as far as it has to:

public static void RemoveFirst<T>(LinkedList<T> list, Predicate<T> predicate)
{
    var node = list.First;
    while (node != null)
    {
        if (predicate(node.Value))
        {
            list.Remove(node);
            return;
        }
        node = node.Next;
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

In case someone need same thing, but for IList<T> (Inspired by Strillo answer, but more efficient)

public bool Remove(this IList<T> list, Predicate<T> predicate)
{
    for(int i = 0; i < list.Count; i++)
    {
        if(predicate(list[i]))
        {
            list.RemoveAt(i);
            return true;
        }                   
    }   

    return false;
}
tigrou
  • 4,236
  • 5
  • 33
  • 59