Skip to main content
5 of 9
added 83 characters in body
user avatar
user avatar

As slepic in his comment, I also wonder why you use and enumerator in the first and foreach in the second place?


You can eliminate null checks in the versions that call other overrides:

public static int DistinctCount<TSource>(this IEnumerable<TSource> source) =>
        source?.DistinctCount((IEqualityComparer<TSource>)null) ?? throw new ArgumentNullException(nameof(source));

can be reduced to:

public static int DistinctCount<TSource>(this IEnumerable<TSource> source) => DistinctCount(source, (IEqualityComparer<TSource>)null);

And the other to:

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) => DistinctCount(source, predicate, null);

You could create a common counter method as:

private static int Count<TSource>(IEnumerable<TSource> source, Func<TSource, ISet<TSource>, bool> predicate, IEqualityComparer<TSource> comparer)
{
  ISet<TSource> set = new HashSet<TSource>(comparer);
  int num = 0;

  using (IEnumerator<TSource> enumerator = source.GetEnumerator())
  {
    while (enumerator.MoveNext())
    {
      if (predicate(enumerator.Current, set))
      {
        checked
        {
          ++num;
        }
      }
    }
  }

  return num;
}

Called in the first as:

public static int DistinctCount<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
  if (source is null)
  {
    throw new ArgumentNullException(nameof(source));
  }

  return Count(source, (o, set) => set.Add(o), comparer);
}

and in the latter as:

public static int DistinctCount<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate,
    IEqualityComparer<TSource> comparer)
{
  if (source is null)
  {
    throw new ArgumentNullException(nameof(source));
  }

  if (predicate is null)
  {
    throw new ArgumentNullException(nameof(predicate));
  }

  return Count(source, (o, set) => predicate(o) && set.Add(o), comparer);
}

But do you really need num? Couldn't you just return set.Count?


By using ToHashSet<T>() directly as show below I only find a minor loss in performance compared to your versions:

  public static class ExtensionsReview
  {
    public static int DistinctCount<TSource>(this IEnumerable<TSource> source) => DistinctCount(source, (IEqualityComparer<TSource>)null);

    public static int DistinctCount<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
    {
      if (source is null)
      {
        throw new ArgumentNullException(nameof(source));
      }

      return source.ToHashSet(comparer).Count;
    }

    public static int DistinctCount<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) => DistinctCount(source, predicate, null);

    public static int DistinctCount<TSource>(
        this IEnumerable<TSource> source,
        Func<TSource, bool> predicate,
        IEqualityComparer<TSource> comparer)
    {
      if (source is null)
      {
        throw new ArgumentNullException(nameof(source));
      }

      if (predicate is null)
      {
        throw new ArgumentNullException(nameof(predicate));
      }

      return source.Where(predicate).ToHashSet(comparer).Count;

    }
  }
user73941