question

AndrewWatts-2290 avatar image
0 Votes"
AndrewWatts-2290 asked ·

DIstinct IEqualityComparer against property

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();
         }
     }

dotnet-csharp
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

TimonYang-MSFT avatar image
0 Votes"
TimonYang-MSFT answered ·

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.

· 2 ·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi I changed the code of GetHashCode as you suggested to try either Obj.Name.GetHashCode then 1
but neither changed the results. Only your distinctItemsName1 ran correctly. Did you get a different result ??

   public int GetHashCode(Item obj)
         {
             // return obj.Id.GetHashCode();// ^
             return 1;  //obj.Name.GetHashCode();
         }
    
 var distinctItemsName= itemsName.Distinct(new DistinctItemComparerOnName());
    
             int diName = distinctItemsName.Count();  // still returns all 3 !
             var distinctItemsName1 = itemsName.GroupBy(item => item.Name).Select(g => g.First());  // ONLY this worked 
0 Votes 0 ·

I got the correct result using both methods.

     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.Name.GetHashCode();
         }
     }

80003-13.png

0 Votes 0 ·
13.png (13.1 KiB)
karenpayneoregon avatar image
0 Votes"
karenpayneoregon answered ·

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)}");
             }
    
    
     }
    
    
    
 }



f1.png (16.3 KiB)
·
10 |1000 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.