DIstinct IEqualityComparer against property

Andrew Watts 41 Reputation points
2021-03-17T20:26:41.663+00:00

The simple console app code below doesnt work - my assumption was the evaluation would be based solely on the custom Equals criteria - as it evaluates equal based only on name not another property. But when the var distinctItemsName= itemsName.Distinct(new DistinctItemComparerOnName()); is used , I get the entire 3 objects back instead of 1. Where have I gone wrong and what to make it work/why.

Thanks

class DistinctItemComparerOnName : IEqualityComparer<Item>
{

        public bool Equals(Item x, Item y)
        {
            bool retVal = false;
            retVal = x.Name == y.Name;
            return retVal;
        }

        public int GetHashCode(Item obj)
        {
            return obj.Id.GetHashCode() ^
                obj.Name.GetHashCode();
        }
    }
    class Item
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

  class Program
    {
        static void Main(string[] args)
        {

            List<Item> itemsName= new List<Item>();

            for (int i = 0; i < 3; i++)
            {
                Item item = new Item();
                item.Id = i.ToString();
                item.Name = "foo";   // now we want to have the distinct where name is different ...
                itemsName.Add(item);
            }

            var distinctItemsName= itemsName.Distinct(new DistinctItemComparerOnName());

            int diName = distinctItemsName.Count();

            Console.ReadKey();
        }
    }
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,633 questions
0 comments No comments
{count} votes

Accepted answer
  1. Timon Yang-MSFT 9,586 Reputation points
    2021-03-18T02:19:14.15+00:00

    When judging whether two objects are equal, we need to know: If the two objects are equal, the GetHashCode method of each object must return the same value. However, if the two objects are not equal, the GetHashCode method of the two objects does not have to return different values.

    Due to the difference in ID, the value returned by your GetHashCode method is different, so it judges that these are two different objects regardless of how your equals method is written.

    If you change the GetHashCode method to return obj.Name.GetHashCode(); or change it to return 1; directly, the result will be what you want.

    But if there are no other requirements, custom DistinctItemComparerOnName is not necessary, you can simply use linq to get the same result.

                var distinctItemsName1 = itemsName.GroupBy(item => item.Name).Select(g => g.First());  
    

    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


1 additional answer

Sort by: Most helpful
  1. Karen Payne MVP 35,386 Reputation points
    2021-03-18T01:50:19.67+00:00

    Hello,

    I have a code sample project on GitHub. If you want to download the project which is part of a large repository, make sure to have the current version of Git installed and run the following script.

    mkdir code  
    cd code  
    git init  
    git remote add -f origin https://github.com/karenpayneoregon/code-samples-csharp  
    git sparse-checkout init --cone  
    git sparse-checkout add IEqualityComparer  
    git pull origin master  
    :clean-up  
    del .gitattributes  
    del .gitignore  
    del .yml  
    del .editorconfig  
    del *.md  
    del *.sln  
    

    Classes to look at
    78952-f1.png
    Code usage is in the sole form

    using System;  
    using System.Collections.Generic;  
    using System.ComponentModel;  
    using System.Data;  
    using System.Drawing;  
    using System.Globalization;  
    using System.IO;  
    using System.Linq;  
    using System.Security.AccessControl;  
    using System.Security.Principal;  
    using System.Text;  
    using System.Threading.Tasks;  
    using System.Windows.Forms;  
    using IEqualityComparerApp.Classes;  
      
    namespace IEqualityComparerApp  
    {  
        public partial class Form1 : Form  
        {  
            private List<Person> _persons;  
            public Form1()  
            {  
                InitializeComponent();  
      
                PersonList();  
            }  
      
            private void PersonList()  
            {  
                _persons = MockedData.MockedPersons;  
            }  
      
            private void CustomerByNameCountryCityComparerButton_Click(object sender, EventArgs e)  
            {  
      
                var customers = MockedData.MockedCustomers()  
                    .Distinct(new CustomerByNameCountryCityComparer())  
                    .ToList();  
      
                foreach (var customer in customers)  
                {  
                    Console.WriteLine(customer);  
                }   
            }  
      
            private void NameSurnameEqualityComparerButton_Click(object sender, EventArgs e)  
            {  
                Console.WriteLine("nameSurNameCompareResults results");  
                var nameSurNameCompareResults = _persons.Distinct(new NameSurnameEqualityComparer()).ToList();  
      
                foreach (var person in nameSurNameCompareResults)  
                {  
                    Console.WriteLine(person);  
                }  
      
                Console.WriteLine();  
      
                Console.WriteLine("groupResultsAnonymous results");  
                var groupResultsAnonymous = _persons   
                    .GroupBy((person) => new { person.Name, person.Surname })  
                    .Select((g) => new   
                    {  
                        PersonCount = g.Count(),  
                        FirstName = g.Key.Name,  
                        LastName = g.Key.Surname,  
                        Id = g.FirstOrDefault().Id,  
                        Count = g.Count(),  
                        List = g.ToList()  
                    }).Where(personGroup => personGroup.PersonCount > 1).ToList();  
      
      
                foreach (var personGroup in groupResultsAnonymous)  
                {  
                    Console.WriteLine($"{personGroup.Id} {personGroup.FirstName} {personGroup.LastName} {personGroup.Count -1} => " +   
                                      $"{string.Join(",", personGroup.List.Select(personItem => personItem.Id).ToArray())}");  
                }  
      
                Console.WriteLine();  
      
                Console.WriteLine("groupResults1 results");  
      
                List<PersonGroup1> groupResults1 = _persons  
                    .GroupBy((person) => new { person.Name, person.Surname })  
                    .Select((g) =>   
                        new PersonGroup1()  
                        {  
                            Count = g.Count(),  
                            FirstName = g.Key.Name,  
                            SurName = g.Key.Surname,  
                            PersonsList = g.ToList()  
                        })  
                    .Where(personGroup => personGroup.Count >1)  
                    .ToList();  
      
      
                foreach (var personGroup in groupResults1)  
                {  
                    Console.WriteLine($"{personGroup.FirstName} {personGroup.SurName} => " +   
                                      $"{string.Join(",",personGroup.PersonsList.Select(x => x.Id).ToArray())}");  
                }  
      
                Console.WriteLine();  
      
                Console.WriteLine("groupResults2 results");  
      
                List<PersonGroup> groupResults2 =  _persons  
                    .GroupBy((person) => new { person.Name, person.Surname })  
                    .Select((g) => new PersonGroup  
                    {  
                        PersonCount = g.Count(),  
                        FirstName = g.Key.Name,  
                        LastName = g.Key.Surname,  
                        Id = g.FirstOrDefault().Id,  
                        Count = g.Count(),  
                        PersonsList = g.ToList()  
                    }).Where(personGroup => personGroup.PersonCount > 1).ToList();  
      
      
                foreach (var personGroup in groupResults2)  
                {  
                    Console.WriteLine($"{personGroup.Id} {personGroup.FirstName} {personGroup.LastName} => " +   
                                      $"{string.Join(",", personGroup.PersonsList.Select(x => x.Id).ToArray())}");  
                }  
      
                foreach (var personGroup in groupResults2)  
                {  
                    Console.WriteLine($"{personGroup.Id} {personGroup.FirstName} {personGroup.LastName} => ");  
                    foreach (var person in personGroup.PersonsList.Skip(1))  
                    {  
                        Console.WriteLine($"{person.Id,3}");  
                    }  
                }  
      
                Console.WriteLine();  
      
                Console.WriteLine("groupResults3 results");  
                List<PersonGroup2> groupResults3 = _persons  
                    .GroupBy((person) => new { person.Name, person.Surname })  
                    .Select((g) => new PersonGroup2  
                    {  
                        PersonCount = g.Count(),  
                        FirstName = g.Key.Name,  
                        LastName = g.Key.Surname,  
                        Id = g.FirstOrDefault().Id,  
                        Count = g.Count(),  
                        IdentifiersList = g.Select(x => x.Id).ToList()  
                    }).Where(personGroup => personGroup.PersonCount > 1).ToList();  
      
      
                foreach (var personGroup in groupResults3)  
                {  
                    Console.WriteLine($"{personGroup.Id} {personGroup.FirstName} {personGroup.LastName} => {string.Join(",", personGroup.IdentifiersList)}");  
                }  
      
      
        }  
      
      
      
    }  
    
    0 comments No comments