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 ?
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);