问题
I have a collection which has 96 values. I would like to sum values for 4 sequential indexes. How can I do this using Linq?
Example
collection = {100, 101, 200, 150, 103, 105, 100, 104, .........., 20, 40, 60, 80};
Sum of (100, 101, 200, 150;)
then sum of (103, 105, 100, 104;)
... then sum of (20, 40, 60, 80;)
This means now my new collection will have 24 values.
How can I do this using Linq?
回答1:
We can start with this utility function to Batch
items up based on a given batch size:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
{
List<T> buffer = new List<T>(batchSize);
foreach (T item in source)
{
buffer.Add(item);
if (buffer.Count >= batchSize)
{
yield return buffer;
buffer = new List<T>(batchSize);
}
}
if (buffer.Count > 0)
{
yield return buffer;
}
}
After that it's as simple as:
var query = data.Batch(4)
.Select(batch => batch.Sum());
回答2:
You can group by index/4
to get your sums, like this:
var res = collection
.Select((v,i) => new {v, i})
.GroupBy(p => p.i / 4)
.Select(g => g.Sum(p.v));
回答3:
You can calculate a group index from the index, group on that, and get the sum from the values in each group:
var sums = collection
.Select((n, i) => new { Group = i / 4, Value = n })
.GroupBy(x => x.Group)
.Select(g => g.Sum(y => y.Value));
回答4:
You would need a new extension method Partition
:
public static IEnumerable<IEnumerable<T>> Partition<T>(
this IEnumerable<T> source, int partitionSize)
{
var counter = 0;
var result = new T[partitionSize];
foreach(var item in source)
{
result[counter] = item;
++counter;
if(counter >= partitionSize)
{
yield return result;
counter = 0;
result = new T[partitionSize];
}
}
if(counter != 0)
yield return result.Take(counter);
}
Usage would be:
collection.Partition(4).Select(x => x.Sum())
This is an alternate way to the Batch
method posted by Servy.
回答5:
First, make a way to group your sets by index. In this case I've chosen to use integer division to make elements 0-3 group 0, 4-7 group 1, etc.
Next, group your elements in to the different sets that will need summing (by the grouping key).
Finally, select the sum of the elements that belong to each group.
values.Select((x, i) => new { GroupingKey = i/4, Value = x })
.GroupBy(x => x.GroupingKey)
.Select(x => new { Group = x.Key, Sum = x.Sum() });
回答6:
This do that:
static IEnumerable<int> BatchSum(int batchSize, IEnumerable<int> collection)
{
var batch = collection.Take(batchSize).ToList();
if (batch.Count == 0) yield break;
yield return batch.Sum();
var rest = collection.Skip(batchSize);
foreach (var sum in BatchSum(batchSize, rest)) yield return sum;
}
And to use it:
var collection = new[] { 100, 101, 200, 150, 103, 105, 100, 104, 20, 40, 60, 80, 11, 13 };
foreach (var sum in BatchSum(4, collection)) Show(sum);
And the output would be:
551
412
200
24
As you see, your collection length has not to be a factor of batchSize
.
来源:https://stackoverflow.com/questions/17050413/use-linq-to-sum-by-index