tags:

views:

150

answers:

1

I have a list of elements and want to takeWhile the sum (or any aggregation of the elements) satisfy a certain condition. The following code does the job, but i am pretty sure this is not an unusual problem for which a proper pattern should exist.

var list = new List<int> { 1, 2, 3, 4, 5, 6, 7 };
int tmp = 0;
var listWithSum = from x in list
                  let sum = tmp+=x
                  select new {x, sum};
int MAX = 10;
var result = from x in listWithSum
             where x.sum < MAX
             select x.x;

Does somebody know how to solve the task in nicer way, probably combining TakeWhile and Aggregate into one query?

Thx

+1  A: 

It seems to me that you want something like the Scan method from Reactive Extensions (the System.Interactive part) - it's like Aggregate, but yields a sequence instead of a single result. You could then do:

var listWithSum = list.Scan(new { Value = 0, Sum = 0 },
         (current, next) => new { Value = next,
                                  Sum = current.Sum + next });

var result = listWithSum.TakeWhile(x => x.Sum < MaxTotal)
                        .Select(x => x.Value);

(MoreLINQ has a similar operator, btw - but currently it doesn't support the idea of the accumulator and input sequence not being the same type.)

Jon Skeet
That seems to be a proper solution - avoiding the side effect on the tmp variable. I wasn't aware of Reactive Extensions.Another problem i didn't mention above is that by introducing the intermediate result (the anonymous type) the result contains the seed value of the aggregate/scan, which is not part of the initial list.
@martinweser: If you need to skip the first result, that's easy to do - but I don't *think* you need to with Scan.
Jon Skeet