Any, Single, Multiple and Count

Problem

In a collection we can easily check if there is any (particular) item:

                        
                                
      int[] numbers = { 5, 4, 1, 4, 9, 8, 6, 7, 2, 0 };
      var anyNumbers = numbers.Any();
      var anyNumber6 = numbers.Any(n => n == 6);
      
                        
                    

This is a very efficient way to check because it doesn't enumerate the whole collection. After enumerating the first element, the Any() method returns the result. This saves time if the collection is large or enumeration goes slow.

We still have to enumerate the whole collection if we need to know if a collection has one element, multiple elements or we need to compare the count. This is not efficient.

Solution

I created methods that counts the elements but not the whole collection. You can use it like this:

                        
                                
      var numberIsSingle = numbers.IsSingle();
      var numberIsMultiple = numbers.IsMultiple(n => n == 4);
      var isExact = numbers.CountEquals(4);
      var isMany = numbers.CountGreaterThan(4);
      var isFew = numbers.CountGreaterThan(4, n => n <= 2);
      
                        
                    

This is the implementation of the methods

                        
                                
      public static bool IsSingle<T>(this IEnumerable<T> source)
      {
          return source.CountEquals(1); ;
      }
 
      public static bool IsMultiple<T>(this IEnumerable<T> source)
      {
          return source.CountGreaterThan(1);
      }
       
      public static bool IsSingle<T>(this IEnumerable<T> source, Func<T, bool> predicate)
      {
          return source.CountEquals(1, predicate);
      }
       
      public static bool IsMultiple<T>(this IEnumerable<T> source, Func<T, bool> predicate)
      {
          return source.CountGreaterThan(1, predicate);
      }
       
      public static bool CountGreaterThan<T>(this IEnumerable<T> source, int value)
      {
          return source.CountCompareTo(value) > 0;
      }
       
      public static bool CountEquals<T>(this IEnumerable<T> source, int value)
      {
          return source.CountCompareTo(value) == 0;
      }
       
      public static bool CountLowerThan<T>(this IEnumerable<T> source, int value)
      {
          return source.CountCompareTo(value) < 0;
      }
       
      public static bool CountGreaterThan<T>(this IEnumerable<T> source, int value, Func<T, bool> predicate)
      {
          return source.CountCompareTo(value, predicate) > 0;
      }
       
      public static bool CountEquals<T>(this IEnumerable<T> source, int value, Func<T, bool> predicate)
      {
          return source.CountCompareTo(value, predicate) == 0;
      }
       
      public static bool CountLowerThan<T>(this IEnumerable<T> source, int value, Func<T, bool> predicate)
      {
          return source.CountCompareTo(value, predicate) < 0;
      }
 
      private static int CountCompareTo<T>(this IEnumerable<T> source, int value, Func<T, bool> predicate)
      {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (predicate == null)
        {
            throw new ArgumentNullException("predicate");
        }
     
        int count = 0;
        using (IEnumerator<T> enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                T item = enumerator.Current;
                if (predicate(item)) count++; // The predicate is true
                if (count > value) break; // No further counting needed
            }
        }
        return count.CompareTo(value);
      }
 
      private static int CountCompareTo<T>(this IEnumerable<T> source, int value)
      {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
     
        var collectionT = source as ICollection<T>;
        if (collectionT != null)
        {
            return collectionT.Count.CompareTo(value);
        }
        var collection = source as ICollection;
        if (collection != null)
        {
            return collection.Count.CompareTo(value);
        }
     
        int count = 0;
        using (IEnumerator<T> enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                count++;
                if (count > value) break; // Now you can draw a conclusion, with < it is actually 1 too much
            }
        }
        return count.CompareTo(value);
      }
      
                        
                    

Leave a Comment

Comment

Comments

C# CSharp Blog Comment

Frank Bakker 27-05-2012 / Reply

Hi Alex,

This indeed is handy to have around. I think you can reduce some redundant code by using 'pain old' Where() to filter in the overload with the predicate

private static int CountCompareTo<T>(this IEnumerable<T> source, int value, Func<T, bool> predicate)
{
    return source.where(predicate).CountCompareTo(source, value);
}

Regards Frank


C# CSharp Blog Comment Reply

Alex Siepman 01-07-2012 / Reply

Hi Frank,

Thanks for your suggestion. This saves a lot of code. Less code means less errors, less maintenance...
I will update the production code ald leave this this post as posted.

Regards,

Alex


C# CSharp Blog Comment

Marleen Seidner 02-09-2013 / Reply

Wohh exactly what I was looking for, thank you for putting up.