Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does C# linq have something similar to SummaryStatistics?

Tags:

c#

.net

linq

In Java I can do this:

List<String> stringList = getRandomStrings(100_000, 1000);
IntSummaryStatistics stats =
                stringList.stream()
                        .filter(Objects::nonNull)
                        .mapToInt(String::length)
                        .summaryStatistics();

System.out.println(stats);

The output is:

IntSummaryStatistics{count=100000, sum=49868013, min=0, average=498.680130, max=999}

Does linq have the same functionality? Or do I have to calculate everything seperately?

Java does this in one go, and it looks like in C# you have to calculate all the constituents Average(), Count(), Sum(), Min() and Max() separately.

like image 321
Coder-Man Avatar asked Dec 10 '22 05:12

Coder-Man


1 Answers

I'm not aware of anything built into .NET that does the equivalent, but you can easily implement it yourself using Aggregate from LINQ. This meets your requirement of only enumerating the source collection once.

var ints = new List<int>() { 1, 2, 3, 4, 677, 8 };

var summary = ints.Aggregate(
    seed: (
        count: 0, 
        sum: 0, 
        min: int.MaxValue, 
        max: int.MinValue),
    func: (acc, x) => (
        count: acc.count + 1, 
        sum: acc.sum + x, 
        min: Math.Min(acc.min, x), 
        max: Math.Max(acc.max, x)),
    resultSelector: acc => (
        acc.count, 
        acc.sum, 
        acc.min, 
        acc.max, 
        avg: (double)acc.sum / acc.count));

Console.WriteLine(
    $"count = {summary.count}, " +
    $"sum = {summary.sum}, " +
    $"min = {summary.min}, " +
    $"max = {summary.max}, " +
    $"average = {summary.avg}");

If your collection is very large and you want to parallelize its processing, you can use this overload:

var summary = ints.AsParallel().Aggregate(
    seed: (
        count: 0,
        sum: 0,
        min: int.MaxValue,
        max: int.MinValue),
    updateAccumulatorFunc: (acc, x) => (
        count: acc.count + 1,
        sum: acc.sum + x,
        min: Math.Min(acc.min, x),
        max: Math.Max(acc.max, x)),
    combineAccumulatorsFunc: (acc1, acc2) => (
        count: acc1.count + acc2.count,
        sum: acc1.sum + acc2.sum,
        min: Math.Min(acc1.min, acc2.min),
        max: Math.Max(acc1.max, acc2.max)),
    resultSelector: acc => (
        acc.count,
        acc.sum,
        acc.min,
        acc.max,
        avg: (double)acc.sum / acc.count));
like image 53
Douglas Avatar answered Dec 12 '22 19:12

Douglas