LINQ lambda expressions in universal generic equality comparer
Submitted by Tomasz Paprocki on 21 September, 2010 - 19:18
Implementation
Below you can find implementation we use as universal equality comparer. Only lambda expressions as in LINQ queries are needed to measure items. Class LambdaComparer is generic, so you can use it on any type you want to.LambdaComparer class
using System; using System.Collections.Generic; namespace Karmian.Framework.Helpers { public class LambdaComparer<T> : IEqualityComparer<T> { public LambdaComparer(Func<T, T, bool> equals, Func<T, int> getHashCode) { _equals = equals; _getHashCode = getHashCode; } readonly Func<T, T, bool> _equals; public bool Equals(T x, T y) { return _equals(x, y); } readonly Func<T, int> _getHashCode; public int GetHashCode(T obj) { return _getHashCode(obj); } } }
Imports System.Collections.Generic Namespace Karmian.Framework.Helpers Public Class LambdaComparer(Of T) Implements IEqualityComparer(Of T) Public Sub New(equals As Func(Of T, T, Boolean), getHashCode As Func(Of T, Integer)) _equals = equals _getHashCode = getHashCode End Sub ReadOnly _equals As Func(Of T, T, Boolean) Public Overloads Function Equals(x As T, y As T) As Boolean Implements IEqualityComparer(Of T).Equals Return _equals(x, y) End Function ReadOnly _getHashCode As Func(Of T, Integer) Public Overloads Function GetHashCode(obj As T) As Integer Implements IEqualityComparer(Of T).GetHashCode Return _getHashCode(obj) End Function End Class End Namespace
Usage example
City class used as comparison item
public class City { public string Name { get; set; } public int Population { get; set; } public override string ToString() { return string.Format("\tName: {0}\t, Population: {1}", Name, Population); } }
Public Class City Public Property Name() As String Get Return m_Name End Get Set m_Name = Value End Set End Property Private m_Name As String Public Property Population() As Integer Get Return m_Population End Get Set m_Population = Value End Set End Property Private m_Population As Integer Public Overrides Function ToString() As String Return String.Format(vbTab & "Name: {0}" & vbTab & ", Population: {1}", Name, Population) End Function End Class
Application showing typical use
class Program { private static void Main() { var cities = CreateCities(); Console.WriteLine("Normal listing:"); foreach (var city in cities) Console.WriteLine(city); Console.WriteLine("Press any key to continue...\n"); Console.ReadKey(true); Console.WriteLine("Distinct by name:"); foreach (var city in cities.Distinct( new LambdaComparer<City>( (c1, c2) => c1.Name.Equals(c2.Name), c => 1 /* force compare other than by instance */))) Console.WriteLine(city); Console.WriteLine("Press any key to continue...\n"); Console.ReadKey(true); Console.WriteLine("More than one property is doubled:"); foreach (var city in cities.Distinct( new LambdaComparer<City>( (c1, c2) => c1.Population.Equals(c2.Population) || c2.Name.Equals(c2.Name), c => 1 /* force compare other than by instance */))) Console.WriteLine(city); Console.WriteLine("Press any key to continue...\n"); Console.ReadKey(true); Console.WriteLine("Contains city with population of 789? :"); Console.WriteLine(cities.Contains( new City { Name = "City789", Population = 789 }, new LambdaComparer<City>( (c1, c2) => c1.Population.Equals(c2.Population), c => c.GetHashCode() /* instance has nothing to do here */))); Console.WriteLine("Press any key to continue...\n"); Console.ReadKey(true); Console.WriteLine("HashSet with equality set to City.Name property? :"); var hashSet = new HashSet<City>( new LambdaComparer<City>( (c1, c2) => c1.Name.Equals(c2.Name), c => 1 /* force compare other than by instance */)) { new City {Name = "aaa"}, new City {Name = "aba"}, new City {Name = "aaa"}, new City {Name = "baa"}, new City {Name = "aba"} }; foreach (var city in hashSet) Console.WriteLine(city); Console.WriteLine("Press any key to exit...\n"); Console.ReadKey(true); } private static IEnumerable<City> CreateCities() { return new List<City> { new City { Name = "City1", Population = 123 }, new City { Name = "City2", Population = 456 }, new City { Name = "City1", Population = 789 }, new City { Name = "City3", Population = 123 }, new City { Name = "City4", Population = 987 }, new City { Name = "City4", Population = 654 } }; } }
Class Program Private Shared Sub Main() Dim cities = CreateCities() Console.WriteLine("Normal listing:") For Each city As var In cities Console.WriteLine(city) Next Console.WriteLine("Press any key to continue..." & vbLf) Console.ReadKey(True) Console.WriteLine("Distinct by name:") ' force compare other than by instance For Each city As var In cities.Distinct(New LambdaComparer(Of City)(Function(c1, c2) c1.Name.Equals(c2.Name), Function(c) 1)) Console.WriteLine(city) Next Console.WriteLine("Press any key to continue..." & vbLf) Console.ReadKey(True) Console.WriteLine("More than one property is doubled:") ' force compare other than by instance For Each city As var In cities.Distinct(New LambdaComparer(Of City)(Function(c1, c2) c1.Population.Equals(c2.Population) OrElse c2.Name.Equals(c2.Name), Function(c) 1)) Console.WriteLine(city) Next Console.WriteLine("Press any key to continue..." & vbLf) Console.ReadKey(True) Console.WriteLine("Contains city with population of 789? :") ' instance has nothing to do here Console.WriteLine(cities.Contains(New City() With { _ Key .Name = "City789", _ Key .Population = 789 _ }, New LambdaComparer(Of City)(Function(c1, c2) c1.Population.Equals(c2.Population), Function(c) c.GetHashCode()))) Console.WriteLine("Press any key to continue..." & vbLf) Console.ReadKey(True) Console.WriteLine("HashSet with equality set to City.Name property? :") ' force compare other than by instance Dim hashSet = New HashSet(Of City)(New LambdaComparer(Of City)(Function(c1, c2) c1.Name.Equals(c2.Name), Function(c) 1)) From { _ New City() With { _ Key .Name = "aaa" _ }, _ New City() With { _ Key .Name = "aba" _ }, _ New City() With { _ Key .Name = "aaa" _ }, _ New City() With { _ Key .Name = "baa" _ }, _ New City() With { _ Key .Name = "aba" _ } _ } For Each city As var In hashSet Console.WriteLine(city) Next Console.WriteLine("Press any key to exit..." & vbLf) Console.ReadKey(True) End Sub Private Shared Function CreateCities() As IEnumerable(Of City) Return New List(Of City)() From { _ New City() With { _ Key .Name = "City1", _ Key .Population = 123 _ }, _ New City() With { _ Key .Name = "City2", _ Key .Population = 456 _ }, _ New City() With { _ Key .Name = "City1", _ Key .Population = 789 _ }, _ New City() With { _ Key .Name = "City3", _ Key .Population = 123 _ }, _ New City() With { _ Key .Name = "City4", _ Key .Population = 987 _ }, _ New City() With { _ Key .Name = "City4", _ Key .Population = 654 _ } _ } End Function End Class
Output
Normal listing: Name: City1 , Population: 123 Name: City2 , Population: 456 Name: City1 , Population: 789 Name: City3 , Population: 123 Name: City4 , Population: 987 Name: City4 , Population: 654 Press any key to continue... Distinct by name: Name: City1 , Population: 123 Name: City2 , Population: 456 Name: City3 , Population: 123 Name: City4 , Population: 987 Press any key to continue... More than one property is duplicated: Name: City1 , Population: 123 Press any key to continue... Contains city with population of 789? : True Press any key to continue... HashSet with equality set to City.Name property : Name: aaa , Population: 0 Name: aba , Population: 0 Name: baa , Population: 0 Press any key to exit...
Attachment | Size |
---|---|
[binaries] KarmianEqualityComparer.zip | 41.67 KB |
[sources] KarmianEqualityComparer.zip | 46.45 KB |
Comments
Post new comment