Often we need to check if the items in the collections are same or not. It comes into action very often during unit testing.
Here we will see three ways of doing so
Entity Class (Player Entity)
namespace ConsoleApplication1
{
public class Players
{
public int PlayerId { get; set; }
public string PlayerName { get; set; }
public string BelongsTo { get; set; }
public int PlayerAge { get; set; }
public int FeePerMatch { get; set; }
}
}
And let the have our source ready as under
private static List GetPlayerList1()
{
List lstPlayers = new List();
Enumerable
.Range(1, 10)
.ToList()
.ForEach(i => lstPlayers.Add(new Players
{
PlayerId = i
,
PlayerName = string.Concat("PlayerName", i)
,
BelongsTo = i % 2 == 0 ? "India" : "USA"
,
PlayerAge = i + 20
,
FeePerMatch = i + 1000
}));
return lstPlayers;
}
private static List GetPlayerList2()
{
List lstPlayers = new List();
Enumerable
.Range(1, 10)
.ToList()
.ForEach(i => lstPlayers.Add(new Players
{
PlayerId = i
,
PlayerName = string.Concat("PlayerName", i)
,
BelongsTo = i % 2 == 0 ? "India" : "USA"
,
PlayerAge = i + 20
,
FeePerMatch = i + 1000
}));
return lstPlayers;
}
}
Approach 1 : Using Union extension method
var source1 = GetPlayerList1();
var source2 = GetPlayerList2();
var result = source1.Where(x1 => !source2.Any(x2 => x1.PlayerId == x2.PlayerId
&& x1.PlayerName == x2.PlayerName
&& x1.BelongsTo == x2.BelongsTo
&& x1.PlayerAge == x2.PlayerAge
&& x1.FeePerMatch == x2.FeePerMatch))
.Union(
source2.Where(x1 => !source1.Any(x2 => x1.PlayerId == x2.PlayerId
&& x1.PlayerName == x2.PlayerName
&& x1.BelongsTo == x2.BelongsTo
&& x1.PlayerAge == x2.PlayerAge
&& x1.FeePerMatch == x2.FeePerMatch)));
if (result.Count() > 0) Console.WriteLine("Objects are not equal");
else Console.WriteLine("Objects are equal");
Console.ReadKey(true);
//Output
//Objects are equal
Approach 2 : Using Specific Custom IEqualityComparer <T>
First make a class that implements the IEqualityComparer interface
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class CompareObjects : IEqualityComparer < Players >
{
#region IEqualityComparer <Players > Members
public bool Equals(Players x, Players y)
{
var result = x.PlayerId == y.PlayerId
&& x.PlayerName == y.PlayerName
&& x.BelongsTo == y.BelongsTo
&& x.PlayerAge == y.PlayerAge
&& x.FeePerMatch == y.FeePerMatch;
return result;
}
//A hash code is a numeric value that is used to identify an object during equality testing.
//http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx
//N.B.~ it is not needed by Enumerable.SequenceEqual
public int GetHashCode(Players obj)
{
return (string.Concat(obj.PlayerId,"_",obj.PlayerName, "_",obj.BelongsTo, "_" ,obj.PlayerAge,"_",obj.FeePerMatch)).GetHashCode();
}
#endregion
}
}
Then invoke it as under
var result = Enumerable.SequenceEqual(source1, source2, new CompareObjects());
//OR
//var result2 = source1.SequenceEqual(source2, new CompareObjects());
if (result2) Console.WriteLine("Objects are equal");
else Console.WriteLine("Objects are not equal");
Console.ReadKey(true);
//Output
//Objects are equal
N.B.~ This approach works by assuming that the objects in the collections are in the same order. But if the order changes, then we can go ahead with
var result = source1.Count == source2.Count && source1.Count == source1.Intersect(source2).Count();
N.B.~ We can also use an IEqualityComparer <T> with Intersect extension method
Approach 3 : Using Generic Custom IEqualityComparer <T>
It is similar to the approach 2 but it is generic
First make a class that implements the IEqualityComparer interface
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
public class GenericComparer<T> : IEqualityComparer<T>
{
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
Type type = typeof(T);
if (typeof(IEquatable<T>).IsAssignableFrom(type)) return EqualityComparer<T>.Default.Equals(x, y);
foreach (PropertyInfo prop in type.GetProperties())
{
Type propComparerType = typeof(GenericComparer<>).MakeGenericType(prop.PropertyType);
object propComparer = Activator.CreateInstance(propComparerType);
if (!
((bool)typeof(IEqualityComparer<>)
.MakeGenericType(prop.PropertyType)
.GetMethod("Equals")
.Invoke(propComparer, new object[] { prop.GetValue(x, null), prop.GetValue(y, null) })
)
)
return false;
}
return true;
}
public int GetHashCode(T obj)
{
PropertyInfo[] arrPropInfo = obj.GetType().GetProperties();
string str = string.Empty;
Enumerable.Range(0, arrPropInfo.Length)
.ToList()
.ForEach(i => str += string.Concat(arrPropInfo[i].Name, "_"));
str = str.Substring(0, str.Length - 1);
return str.GetHashCode();
}
#endregion
}
}
Then invoke it as under
var result = Enumerable.SequenceEqual(source1, source2, new GenericComparer<Players>());
if (result) Console.WriteLine("Objects are equal");
else Console.WriteLine("Objects are not equal");
Console.ReadKey(true);
Hope this helps