tags:

views:

138

answers:

2

Is it possible to modify elements of a collection, say List, LinkedList, etc using something like:

collection.Do ( action )

which does something like:

item.Value = 0
(for each item)

I know enumerators only give the value, not the reference, hence the question.

How would one do this using LINQ?

+5  A: 

Try:

void ModifyEach<T>(this IList<T> list, Func<T, T> modifier)
{
    for (int n = 0; n < list.Count; n++)
        list[n] = modifier(list[n]);
}

Usage:

List<int> x = new List<int> { 1, 3, 7 };

x.ModifyEach(n => n + 1); // increment each item
Daniel Earwicker
Thanks, same would work for IEnumerable, right?
Joan Venge
Not unless you use IEnumerable.ElementAt(), because by default IEnumerable doesn't provide random access. You can't use foreach because that would modify the collection.
Andy
No, it can only work with a collection interface that allows you to modify the contents of the list at a specific position, which basically means IList or IDictionary.
Daniel Earwicker
@Andy, ElementAt is no use; what's needed is the ability to modify the collection, not random access (I'm only using the [int] indexer to allow modification).
Daniel Earwicker
Couldn't you just do x = x.Select(n => n + 1).ToList(); ?
Cameron MacFarland
@Cameron, That would create a new list rather than doing an in-place modification of the existing list.
LukeH
@Luke: True, I wasn't sure if that made a difference to the original poster or not.
Cameron MacFarland
+2  A: 

If your collection contains reference types then you can modify its items in-place by using an extension method, similar to the ForEach method on List<T>.

This is possible because you're not actually trying to alter the IEnumerable<T> sequence: The collection contains references but you're only modifying the referenced objects, not the references themselves.

This technique won't work for collections of value types. In that case you would be trying to alter the read-only, IEnumerable<T> sequence, which isn't possible.

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (T item in source)
    {
        action(item);
    }
}

// ...

public class Example
{
    public int Value { get; set; }
}

// ...

Example[] examples =
    {
        new Example { Value = 1 }, new Example { Value = 2 },
        new Example { Value = 3 }, new Example { Value = 4 }
    };

examples.ForEach(x => x.Value *= 2);

// displays 2, 4, 6, 8
foreach (Example e in examples)
{
    Console.WriteLine(e.Value);
}
LukeH