1

In this case, I have two different LINQ expressions to get count from Products for two different conditions. I was just curious if there could be anyway of retrieving these two counts from one LINQ expression?

class Program
{
    static void Main(string[] args)
    {
        List<Product> Products = new List<Product>()
        {
            new Product() { ID = 1 },
            new Product() { ID = 2 },
            new Product() { ID = 3 },
            new Product() { ID = 4 },
            new Product() { ID = 5 },
            new Product() { ID = 6 }
        };

        int all = Products.Count();
        int some = Products.Where(x => x.ID < 2).Count();
    }
}

public class Product
{
    public int ID { get; set; }
}
bash0r
  • 774
  • 6
  • 17
paul deter
  • 857
  • 2
  • 7
  • 20

2 Answers2

6

Using Aggregate you can avoid iterating through your collection twice:

var result = Products.Aggregate(new {a=0, s=0},(p,c) => 
                   { 
                       return new { a = p.a + 1, s = c.ID < 2 ? p.s + 1 : p.s };
                   });

Now result.a == 6 and result.s == 2

You can, of course, create a class to hold your result if you want instead of using an anonymous type, and it works much the same way. That maybe easier to deal with if you have to return it from a function, for example.

So you could do something like:

public class CountResult
{
    public int All { get; set; }
    public int Some { get; set; }
}

public CountResult GetMyCount(IEnumerable<Product> products)
{
    return products.Aggregate(new CountResult(), (p,c) => 
    {
        p.All++;
        if (c.ID < 2)   // or whatever you condition might be
        {
           p.Some++;
        }
        return p;
    });
}
Slai
  • 22,144
  • 5
  • 45
  • 53
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
0

You can do this using a Tuple<int, int>:

var result = new Tuple<int, int>(Products.Count, Products.Where(x => x.ID < 2).Count());

And plese remark the use of Products.Count property which has O(1) complexity instead of O(N), so you don't have to worry at all about the performance of this implementation. For further reading you can check this post.

Community
  • 1
  • 1
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
  • 1
    this can be expensive ; each item is iterated twice (and in case of a **not recommended** side-effet sequence [silly example : a sequence of random values] the 2 counts will be over different sequence) – Sehnsucht Oct 13 '16 at 20:53
  • @meJustAndrew It's just a comment. Imagine the 1,000 users viewing this post in the coming year, some will want a performant solution while others won't care. I.e., I don't think he is suggesting you remove your post, just clarifying that he doesn't recommend it on performance grounds as well as other unintended consequences. – Quantic Oct 13 '16 at 20:58
  • @Quantic I have updated my answer so it reveals performance improvements. – meJustAndrew Oct 13 '16 at 22:04
  • @Sehnsucht you can recheck my answer, and thanks for the remark. The first version had indeed performance deficiences. – meJustAndrew Oct 13 '16 at 22:05
  • @Sehnsucht: agree with you there - it might make sense to do a greedy fetch by doing a `Product.ToList()` and then running the counts, else unexpected things may happen... as they always do with iterators... – code4life Oct 13 '16 at 22:14
  • *"Products.Count property which has O(1)"* this is true if `Products` is a `List` (which it is in this example), but this is not generally true of all `IEnumerable`. – Matt Burland Oct 14 '16 at 12:41