paroxit
paroxit

Reputation: 667

New Distinct Extension Method for IEnumerable<T>

I've been trying to add a new extension method for to distinct an IEnumerable<T> object. For both learning and applying purposes.

The logic behind should do something like this : (this works)

 // sis is a DbContext by the way
 List<MyObj> objs = sis.MyObjs.ToList();

 // objs contains duplicate entities

 List<MyObj> _objs = new List<MyObj>();

 foreach(MyObj e in MyObjs)
 {
     if (_ems.Contains(e) == false) _ems.Add(e);
 }


 foreach(MyObj e in _ems)
 {
     Console.WriteLine(e.ID); // Distinction happens
 }

I've wrote a new extension method to do the same as above lines.

 public static IEnumerable<T> Distinct<T>(this IEnumerable<T> en)
 {
     foreach(T f in en)
     {
         if (en.Contains(f) == false) yield return f;
     }
 }

but it didn't work. And the strange thing is, I've also tried these (separately)

  objs.Distinct(); // default Distinct() method in Linq
  objs.GroupBy(t => t.ID).Select(t => t.FirstOrDefault());

but they also couldn't have distinct the objects. The only thing works, first logic that i wrote above.

So, how could one possibly write a new extension to do the same thing as first logic does ?

Upvotes: 0

Views: 799

Answers (2)

Jodrell
Jodrell

Reputation: 35726

This all depends on how T implements Equals and GetHashCode. If the implementation is the default inherited from object then this will be reference equality.

Unless T has a level of immutability like string, then all instances will be different, they'll have different references.

You can add an overload to your Distinct method to accept an IEqualityComparer<T> implementation to override Ts behaviour.

Additionaly, your current implementation is more of a existence stripper, see a proposed alternative below.

public static IEnumerable<T> Distinct<T>(
        this IEnumerable<T> source,
        IEqualityComparer<T> comparer = null)
{
    if (comparer == null)
    {
        comparer = EqualityComparer<T>.Default;
    }

    var seen = new HashSet<T>(comparer);

    foreach (var t in source)
    {
        if (seen.Contains(t))
        {
            continue;
        }

        yield return t;
        seen.Add(t);
    }
}

Upvotes: 3

Alex Peta
Alex Peta

Reputation: 1407

When using List and Dictionaries always remember to override Equals and GetHashCode in the T entity that you wish to use.

In your example above you are comparing references (addresses) and not the intended values of those addresses.

Upvotes: 0

Related Questions