Nick
Nick

Reputation: 6588

Linq Select and Aggregate within a single iteration

Is there a way to do this with linq without enumerating the fooCollection twice?

var fooCollection = // get foo
var selectedIds = new List<int>();
var aggregateContent = String.Empty;

foreach (var f in foo)
{
    selectedIds.Add(foo.Id);
    aggregateContent += foo.Content
}

var results = new FooResults
{
    Content = aggregateContent,
    SelectedIds = selectedIds
};

return results;

Upvotes: 10

Views: 17766

Answers (5)

Thaina Yu
Thaina Yu

Reputation: 1512

I can't find it too so I write my own like this

public static IEnumerable<V> SelectAggregate<T,V>(this IEnumerable<T> items,V seed,Func<V,T,V> func)
{
    foreach(var item in items)
    {
        seed    = func(seed,item);
        yield return seed;
    }
}


var array = new int[]{ 1,2,3 };
var sumArray = array.SelectAggregate(0,(seed,item) => seed + item).ToArray();
//// { 1,3,6 }

Upvotes: 0

Jens
Jens

Reputation: 25573

Yes, you can use the Enumerable.Aggregate method:

var result = fooCollection.Aggregate(new FooResult(),
                                    (r,f) => 
                                    { 
                                        r.SelectedIds.Add(f.Id);
                                        r.Content += f.Content;
                                        return r;
                                    });

This has the benefit of being side-effect free. I dislike side effects in my LINQ. =)

Upvotes: 18

Roy Dictus
Roy Dictus

Reputation: 33139

You could do this:

foo.ForEach(x => { selectedIds.Add(x.Id); aggregateContent += x.Content; });

I would recommend not concatenating the Content into a string, but to use a StringBuilder instead.

EDIT

If you don't have a LINQ extension library that implements ForEach for IEnumerable, here is a method you can use:

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

Upvotes: 1

user180326
user180326

Reputation:

What you are asking for is to have a Linq statement produce two results. The whole idea of linq is to allow a concise functional programming style without side effects.

If you want multiple results and good performance, you should not be using Linq, and use a regular foreach.

Upvotes: 0

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174349

There is one possiblity, but I consider it a hack:

var aggregateContent = String.Empty;
var selectedIds = foo.Select(x => { aggregateContent += x.Content;
                                    return x.Id; })
                     .ToList();

I would go with the loop you already have. It is much cleaner as any LINQ solution you could come up with.

Upvotes: 2

Related Questions