Skip to main content
sloppy me, I have added some updates to the code with notes.
Source Link
iSR5
  • 5.6k
  • 1
  • 7
  • 14

So, the main implementation would be like this :

private static int DistinctCountIterator<TSource>CountDistinctIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));

    var set = new HashSet<TSource>(comparer);
    var count = 0;
    foreach (TSource element in source)
    {
        checked
        {
            if (set.Add(element) && predicate(element))
            {
                count++;
            }

        }
    }

    return count;
}

Now, it's ofa matter of calling back this method with the appropriate arguments.

public static int DistinctCount<TSource>CountDistinct<TSource>(this IEnumerable<TSource> source)
{
    return DistinctCountIterator<TSource>CountDistinctIterator<TSource>(source, (s) => true, null);
}

public static int DistinctCount<TSource>CountDistinct<TSource>(this IEnumerable<TSource> source,  IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>CountDistinctIterator<TSource>(source, (s) => true, comparer);
}

public static intbool DistinctCount<TSource>AnyDistinct<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    return DistinctCountIterator<TSource>CountDistinctIterator<TSource>(source, predicate, null); == 1;
}

public static intbool DistinctCount<TSource>AnyDistinct<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>CountDistinctIterator<TSource>(source, predicate(s) => true, comparernull); == 1;
}

to something meaningful other than DistinctCount like for instance DistinctAny which return boolean (true if DistinctCount returns 1, false if 0).

UPDATE : I have changed the methods name from DistinctCount to CountDistinct the reason of this is because the method is Counting, so the Count needs to be first so it would be easier to picked up, the other reason is doing this will make it appear after Count on the intellisense list. I also added AnyDistinct methods which replaced the mentioned method (the one with Func<TSource, bool>).

So, the main implementation would like this :

private static int DistinctCountIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));

    var set = new HashSet<TSource>(comparer);
    var count = 0;
    foreach (TSource element in source)
    {
        checked
        {
            if (set.Add(element) && predicate(element))
            {
                count++;
            }

        }
    }

    return count;
}

Now, it's of matter of calling this method with the appropriate arguments.

public static int DistinctCount<TSource>(this IEnumerable<TSource> source)
{
    return DistinctCountIterator<TSource>(source, (s) => true, null);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source,  IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>(source, (s) => true, comparer);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    return DistinctCountIterator<TSource>(source, predicate, null);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>(source, predicate, comparer);
}

to something meaningful other than DistinctCount like for instance DistinctAny which return boolean (true if DistinctCount returns 1, false if 0).

So, the main implementation would be like this :

private static int CountDistinctIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));

    var set = new HashSet<TSource>(comparer);
    var count = 0;
    foreach (TSource element in source)
    {
        checked
        {
            if (set.Add(element) && predicate(element))
            {
                count++;
            }

        }
    }

    return count;
}

Now, it's a matter of calling back this method with the appropriate arguments.

public static int CountDistinct<TSource>(this IEnumerable<TSource> source)
{
    return CountDistinctIterator<TSource>(source, (s) => true, null);
}

public static int CountDistinct<TSource>(this IEnumerable<TSource> source,  IEqualityComparer<TSource> comparer)
{
    return CountDistinctIterator<TSource>(source, (s) => true, comparer);
}

public static bool AnyDistinct<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    return CountDistinctIterator<TSource>(source, predicate, null) == 1;
}

public static bool AnyDistinct<TSource>(this IEnumerable<TSource> source)
{
    return CountDistinctIterator<TSource>(source, (s) => true, null) == 1;
}

to something meaningful other than DistinctCount like for instance DistinctAny which return boolean (true if DistinctCount returns 1, false if 0).

UPDATE : I have changed the methods name from DistinctCount to CountDistinct the reason of this is because the method is Counting, so the Count needs to be first so it would be easier to picked up, the other reason is doing this will make it appear after Count on the intellisense list. I also added AnyDistinct methods which replaced the mentioned method (the one with Func<TSource, bool>).

Source Link
iSR5
  • 5.6k
  • 1
  • 7
  • 14

Slepic and Henrik are wondering about the use of foreach and enumerator, and I'm too.

Anyway, instead of having different versions with actual implementations for the same purpose (count the distinct elements), you can create one private method with the full implementation, and then, just call back this method on the other methods.

So, the main implementation would like this :

private static int DistinctCountIterator<TSource>(IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));

    var set = new HashSet<TSource>(comparer);
    var count = 0;
    foreach (TSource element in source)
    {
        checked
        {
            if (set.Add(element) && predicate(element))
            {
                count++;
            }

        }
    }

    return count;
}

Now, it's of matter of calling this method with the appropriate arguments.

Like this :

public static int DistinctCount<TSource>(this IEnumerable<TSource> source)
{
    return DistinctCountIterator<TSource>(source, (s) => true, null);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source,  IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>(source, (s) => true, comparer);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    return DistinctCountIterator<TSource>(source, predicate, null);
}

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, IEqualityComparer<TSource> comparer)
{
    return DistinctCountIterator<TSource>(source, predicate, comparer);
}

although, for this Distinct I don't see any usage for Func<TSource, bool> predicate except for checking if the element exists or not. As the Distinct would get the unique elements, and if you say element == xxx it'll always return 1 if exists, and 0 if not. Unless there is any other uses except this one, in my opinion, I find it beneficial if rename this method:

DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

to something meaningful other than DistinctCount like for instance DistinctAny which return boolean (true if DistinctCount returns 1, false if 0).