Vahid
Vahid

Reputation: 5444

How to return a specific item in Distinct using EqualityComparer in C#

I have defined a CustomListComparer which compares List<int> A and List<int> B and if Union of the two lists equals at least on of the lists, considers them equal.

var distinctLists = MyLists.Distinct(new CustomListComparer()).ToList();

public bool Equals(Frame other)
{
    var union = CustomList.Union(other.CustomList).ToList();
    return union.SequenceEqual(CustomList) ||
           union.SequenceEqual(other.CustomList);
}

For example, the below lists are equal:

ListA = {1,2,3}
ListB = {1,2,3,4}

And the below lists are NOT:

ListA = {1,5}
ListB = {1,2,3,4}

Now all this works fine. But here is my question: Which one of the Lists (A or B) gets into distinctLists? Do I have any say in that? Or is it all handled by compiler itself?

What I mean is say that the EqualityComparer considers both of the Lists equal. and adds one of them to distinctLists. Which one does it add? I want the list with more items to be added.

Upvotes: 3

Views: 59

Answers (2)

Sriram Sakthivel
Sriram Sakthivel

Reputation: 73472

Distinct always adds the first element which it see. So it depends on the order of the sequence which you passed in.

Source is fairly simple, which can be found here

static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) {
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource element in source)
        if (set.Add(element)) yield return element;
}

If you need to return list with more elements, you need to roll your own. Worth noting that Distinct is lazy, but the implementation you're asking for will need a eager implementation.

static class MyDistinctExtensions
{
    public static IEnumerable<T> DistinctMaxElements<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer) where T : ICollection
    {
        Dictionary<T, List<T>> dictionary = new Dictionary<T, List<T>>(comparer);
        foreach (var item in source)
        {
            List<T> list;
            if (!dictionary.TryGetValue(item, out list))
            {
                list = new List<T>();
                dictionary.Add(item, list);
            }
            list.Add(item);
        }

        foreach (var list in dictionary.Values)
        {
            yield return list.Select(x => new { List = x, Count = x.Count })
                .OrderByDescending(x => x.Count)
                .First().List;
        }
    }
}

Updated the answer with naive implementation, not tested though.

Upvotes: 1

Selman Gen&#231;
Selman Gen&#231;

Reputation: 101701

Instead of Distinct you can use GroupBy with MaxBy method::

var distinctLists = MyLists.GroupBy(x => x, new CustomListComparer())
                           .Select(g => g.MaxBy(x => x.Count))
                           .ToList();

This will group lists using your comparer and select the list that has max item from each group.

MaxBy is quite useful in this situation, you can find it in MoreLINQ library.

Edit: Using pure LINQ:

var distinctLists = MyLists.GroupBy(x => x, new CustomListComparer())
                           .Select(g => g.First(x => x.Count == g.Max(l => l.Count)))
                           .ToList();

Upvotes: 1

Related Questions