Search code examples
c#grouping

C# Grouping with multiple conditions


I have an enumerable t of items that expose a property X which is an integer.
I would like to count the number of elements with X < 0, X == 0 and X > 0.
Sure I can fire three statements like this:

var p = t.Count(a => a.X < 0);
var q = t.Count(b => b.X == 0);
var r = t.Count(c => c.X > 0);

But this trigger CA1851 (Possible multiple enumerations of IEnumerable collection).
What is a better way to get the three numbers via a single enumeration using Linq ?


Solution

  • Another way to solve this is with GroupBy:

    var x = input
           .GroupBy(k=> (
              k  < 0 ? 1 : 0,
              k == 0 ? 1 : 0,
              k  > 0 ? 1 : 0)
            )
           .OrderByDescending(g=>g.Key)
           .Select(k=>k.Count())
           .ToArray();
    
    (int ltz, int zzz, int gtz) val = (x[0], x[1], x[2]);   
    
    

    Commenters astutely pointed out that this approach is missing certain details from the OP and defensive code. Also, agree that Math.Sign does this nicely, was loath to repeat. Here goes:

    var x = t.
        .Select(i=>i.X)
        .Concat(new[] { -1, 0, 1})
        .GroupBy(Math.Sign)
        .OrderBy(k=>k.Key)
        .Select(k=>k.Count())
        .ToArray();
    
       if(x != null && x.Any())
           (int ltz, int zzz, int gtz) val = (x[0]-1, x[1]-1, x[2]-1);