tags:

views:

5896

answers:

11

Is there a way to do the following using Linq:

foreach (var c in collection)
{
   c.PropertyToSet = value;
}

To clarify, I want to iterate through each object in a collection and then update a property on each object.

My use case is I have a bunch of comments on a blog post and I want to iterate through each comment on a blog post and set the datetime on the blog post to be +10 hours. I could do it in SQL, but I want to keep it in the business layer.

+3  A: 

There is no built-in extension method to do this. Although defining one is fairly straight forward. At the bottom of the post is a method I defined called Iterate. It can be used like so

collection.Iterate(c => { c.PropertyToSet = value;} );

Iterate Source

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, (x, i) => callback(x));
}

public static void Iterate<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    if (enumerable == null)
    {
        throw new ArgumentNullException("enumerable");
    }

    IterateHelper(enumerable, callback);
}

private static void IterateHelper<T>(this IEnumerable<T> enumerable, Action<T,int> callback)
{
    int count = 0;
    foreach (var cur in enumerable)
    {
        callback(cur, count);
        count++;
    }
}
JaredPar
Is Iterate necessary, whats wrong with Count, Sum, Avg or other existing extension method that returns a scalar value?
AnthonyWJones
this is pretty close to what i want but a little.. involved. The blog post I posted has a similar implementation but with fewer lines of code.
lomaxx
The IterateHelper seems overkill. The overload that doesn't take an index ends up doing alot more extra work (convert callback to lambda which takes index, keep a count which is never used). I understand it's reuse, but it's a workaround for just using a forloop anyway so it should be efficient.
Cameron MacFarland
@Cameron, IterateHelper serves 2 purposes. 1) Single implementation and 2) allows for ArgumentNullException to be thrown at call time vs. use. C# iterators are delayed executed, having the helper prevents the odd behavior of an exception being thrown during iteration.
JaredPar
@JaredPar: Except you're not using an iterator. There's no yield statement.
Cameron MacFarland
@Cameron, Doh, This is a part of a collection of extension methods I own and it's the only one that doesn't use an iterator. In either case, the extra overhead is negligible.
JaredPar
+1  A: 

No, LINQ doesn't support a manner of mass updating. The only shorter way would be to use a ForEach extension method - http://stackoverflow.com/questions/101265/why-is-there-not-a-foreach-extension-method-on-the-ienumerable-interface#101303

Slace
A: 

I assume you want to change values inside a query so you could write a function for it

void DoStuff()
{
    Func<string, Foo, bool> test = (y, x) => { x.Bar = y; return true; };
    List<Foo> mylist = new List<Foo>();
    var v = from x in mylist
            where test("value", x)
            select x;
}

class Foo
{
    string Bar { get; set; }
}

But not shure if this is what you mean.

Stormenet
This is going in sort of the right direction by would require something to enumerate v, else it'll do nothing.
AnthonyWJones
+1  A: 

My 2 pennies:-

 collection.Count(v => (v.PropertyToUpdate = newValue) == null);
AnthonyWJones
I like the thinking, but it's not really clear what the code is doing
lomaxx
Yeah I'm not sure I'd want this in production code.
Cameron MacFarland
+6  A: 

I actually found an extension method that will do what I want nicely

public static IEnumerable<T> ForEach<T>(
    this IEnumerable<T> source,
    Action<T> act)
{
    foreach (T element in source) act(element);
    return source;
}
lomaxx
nice :) Lomaxx, maybe add an example so peeps can see it in 'action' (boom tish!).
Pure.Krome
+19  A: 

While you can use a ForEach extension method, if you want to use just the framework you can do

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();

The ToList is needed in order to evaluate the select immediately due to lazy evaluation.

Cameron MacFarland
I upvoted this because it's a pretty nice solution... the only reason I like the extension method is that it makes it a little clearer to understand exactly what is going on... however your solution is still pretty sweet
lomaxx
I agree, my answer is mainly for those who don't want to use the extension method.
Cameron MacFarland
If we are creating list anyway, can't we just type:collection.ToList().ForEach(c => c.PropertyToSet = value);
Karol Kolenda
If collection was an `ObservableCollection` say, then changing items in place rather than creating a new list can be useful.
Cameron MacFarland
A: 

You can use LINQ to convert your collection to an array and then invoke Array.ForEach():

Array.ForEach(MyCollection.ToArray(), item=>item.DoSomeStuff());

Obviously this will not work with collections of structs or inbuilt types like integers or strings.

DrJokepu
A: 

Hi,

I want to update and insert some records based on a csv I read. The insert is not a problem, but how to update a bunch of data in a single statement? I have not clear how to join them.

Update Item Set Name = t.Name From Item i, TextFile t Where i.ItemNo = t.ItemNo

For the Name = t.Name I created an private Item UpdateItem(Item originalItem, Item newItem) in which the logic for updating is present.

I want to know to to call this functions and perform an db.SubmitChanges() with the changed records.

Any help appreciated.

A: 

Hi, You can use Magiq (http://magiq.codeplex.com), a batch operation framework for linq. Bye!

ivos
A: 

Here is the extension method I use...

    /// <summary>
    /// Executes an Update statement block on all elements in an  IEnumerable of T
    /// sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="action">The action method to execute for each element.</param>
    /// <returns>The number of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> action)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (action == null) throw new ArgumentNullException("action");
        if (typeof (TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        var count = 0;
        foreach (var element in source)
        {
            action(element);
            count++;
        }
        return count;
    }
Bill Forney
Why "value type elements are not supported by update"?? Nothing interferes that!
abatishchev
That was specific to the project I was working on. I suppose it wouldn't matter in most cases. Lately I've reworked that and renamed it Run(...), removed the value type thing and changed it to return void and dropped the count code.
Bill Forney
That aligns it with Reactive extensions Run method...
Bill Forney
A: 

I've tried a few variations on this, and I keep going back to this guy's solution.

http://www.hookedonlinq.com/UpdateOperator.ashx

Again, this is somebody else's solution. But I've compiled the code into a small library, and use it fairly regularly.

I'm going to paste his code here, for the off chance that his site(blog) ceases to exist at some point in the future. (There's nothing worse than seeing a post that says "Here is the exact answer you need", Click, and Dead URL.)

    public static class UpdateExtensions {

    public delegate void Func<TArg0>(TArg0 element);

    /// <summary>
    /// Executes an Update statement block on all elements in an IEnumerable<T> sequence.
    /// </summary>
    /// <typeparam name="TSource">The source element type.</typeparam>
    /// <param name="source">The source sequence.</param>
    /// <param name="update">The update statement to execute for each element.</param>
    /// <returns>The numer of records affected.</returns>
    public static int Update<TSource>(this IEnumerable<TSource> source, Func<TSource> update)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (update == null) throw new ArgumentNullException("update");
        if (typeof(TSource).IsValueType)
            throw new NotSupportedException("value type elements are not supported by update.");

        int count = 0;
        foreach (TSource element in source)
        {
            update(element);
            count++;
        }
        return count;
    }
}



int count = drawingObjects
        .Where(d => d.IsSelected && d.Color == Colors.Blue)
        .Update(e => { e.Color = Color.Red; e.Selected = false; } );
granadaCoder