Compare equality of two objects based on dictionaries - c#

I have two objects with these definitions:
public static Dictionary<string, Container> cont1 = new Dictionary<string, Container>();
public static Dictionary<string, Container> cont2 = new Dictionary<string, Container>();
The schema of Container class is as following:
public class Container
{
public string IDx { get; set; }
public string IDy { get; set; }
public string Name { get; set; }
public Dictionary<string, Sub> Subs = new Dictionary<string, Sub>();
}
public class Sub
{
public string Namex { get; set; }
public string Namey { get; set; }
public string Value { get; set; }
public Dictionary<string, string> Paths { get; set; }
}
My question is: How can I deep check the equity of cont1 and cont2? I mean the equality of every member and value even deep down within Subs objects;
Is there any functionality in c# for such situations or I have to write a custom method for checking equality based on the structure of the objects myself;
Second Question: I can obviate the equality problem if I can create two different copies of Products; I mean say we have a base Container object with all the members and values and then create two separate copies of Container, namely cont1 and cont2 which changing a value in cont1 wont change the same value in cont2.
Note1: this method for cloning is not working:
cont2 = new Dictionary<string, Container>(cont1);
Note2: most of the proposed methods in other answers are based on a one level dictionary (using loops or LINQ for checking) and not such a case when we have properties and dictionary objects (which having their own properties) within the object.

A Dictionary is a Sequence, so in general what you're probably looking for is Enumerable<T>.SequenceEquals which allows passing in an IEquityComparer<T>.
Your sequence (Dictionary) is an IEnumerable<KeyValuePair<string,Container>> so you need an comparer which implements IEquityComparer<IEnumerable<KeyValuePair<string,Container>>> (Thats a lot of angle braces!).
var equal = cont1.SequenceEquals(cont2, new StringContainerPairEquityComparer());
Note that the order of elements dictionaries is not guaranteed, so to use the method properly you should probably use OrderBy before comparing sequences - however this adds to the inefficiency of this method.
For your second question, what you're trying to do is Clone the dictionary. In general your Container should implement ICloneable interface, which you can then use to create a copy
var cont2 = cont1.ToDictionary(k => k.Key, v => v.Value.Clone());

Yes, you have to write a custom method for checking equality based on the structure of the objects yourself. I would provide a custom IEqualityComparer<Container> and an IEqualityComparer<Sub> like here (GetHashCode implementation based on this):
public class ContainerCheck : IEqualityComparer<Container>
{
private SubCheck subChecker = new SubCheck();
public bool Equals(Container x, Container y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
if (x.IDx != y.IDx || x.IDy != y.IDy || x.Name != y.Name)
return false;
// check dictionary
if (ReferenceEquals(x.Subs, y.Subs))
return true;
if (x.Subs == null || y.Subs == null || x.Subs.Count != y.Subs.Count)
return false;
foreach (var kv in x.Subs)
if (!y.Subs.ContainsKey(kv.Key) || subChecker.Equals(y.Subs[kv.Key], kv.Value))
return false;
return true;
}
public int GetHashCode(Container obj)
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + obj.IDx.GetHashCode();
hash = hash * 23 + obj.IDy.GetHashCode();
hash = hash * 23 + obj.Name.GetHashCode();
foreach (var kv in obj.Subs)
{
hash = hash * 23 + kv.Key.GetHashCode();
hash = hash * 23 + subChecker.GetHashCode(kv.Value);
}
return hash;
}
}
}
public class SubCheck : IEqualityComparer<Sub>
{
public bool Equals(Sub x, Sub y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
if (x.Namex != y.Namex || x.Namey != y.Namey || x.Value != y.Value)
return false;
// check dictionary
if (ReferenceEquals(x.Paths, y.Paths))
return true;
if (x.Paths == null || y.Paths == null || x.Paths.Count != y.Paths.Count)
return false;
foreach(var kv in x.Paths)
if (!y.Paths.ContainsKey(kv.Key) || y.Paths[kv.Key] != kv.Value)
return false;
return true;
}
public int GetHashCode(Sub obj)
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + obj.Namex.GetHashCode();
hash = hash * 23 + obj.Namey.GetHashCode();
hash = hash * 23 + obj.Value.GetHashCode();
foreach (var kv in obj.Paths)
{
hash = hash * 23 + kv.Key.GetHashCode();
hash = hash*23 + kv.Value.GetHashCode();
}
return hash;
}
}
}
This should deep check all properties and the dictionaries. Then you could use following loop to compare both dictionaries with each other:
bool equal = true;
var allKeys = cont1.Keys.Concat(cont2.Keys).ToList();
var containerChecker = new ContainerCheck();
foreach (string key in allKeys)
{
Container c1;
Container c2;
if (!cont1.TryGetValue(key, out c1) || !cont2.TryGetValue(key, out c2))
{
equal = false;
}
else
{
// deep check both containers
if (!containerChecker.Equals(c1, c2))
equal = false;
}
if(!equal)
break; // or collect differences
}

Related

How to check if properties of two objects are equal

I have two objects using the ff. class:
public class Test {
public string Name {get; set;}
public List<Input> Inputs {get;set;}
......
//some other properties I don't need to check
}
public class Input {
public int VariableA {get;set;}
public int VariableB {get;set;}
public List<Sancti> Sancts {get;set;}
}
public class Sancti {
public string Symbol {get;set;}
public double Percentage {get;set;}
}
I want to check if two instance of Test has the same Inputs value. I've done this using a loop but I believe this is not the way to do this.
I've read some links: link1, link2 but they seem gibberish for me. Are there simpler ways to do this, like a one-liner something like:
test1.Inputs.IsTheSameAs(test2.Inputs)?
I was really hoping for a more readable method. Preferrably Linq.
NOTE: Order of inputs should not matter.
One way is to check the set negation between the two lists. If the result of listA negated by listB has no elements, that means that everything in listA exists in listB. If the reverse is also true, then the two lists are equal.
bool equal = testA.Inputs.Except(testB.Inputs).Count() == 0
&& testB.Inputs.Except(testA.Inputs).Count() == 0;
Another is to simply check each element of listA and see if it exists in listB (and vice versa):
bool equal = testA.Inputs.All(x => testB.Inputs.Contains(x))
&& testB.Inputs.All(x => testA.Inputs.Contains(x));
This being said, either of these can throw a false positive if there is one element in a list that would be "equal" to multiple elements in the other. For example, the following two lists would be considered equal using the above approaches:
listA = { 1, 2, 3, 4 };
listB = { 1, 1, 2, 2, 3, 3, 4, 4 };
To prevent that from happening, you would need to perform a one-to-one search rather than the nuclear solution. There are several ways to do this, but one way to do this is to first sort both lists and then checking their indices against each other:
var listASorted = testA.Inputs.OrderBy(x => x);
var listBSorted = testB.Inputs.OrderBy(x => x);
bool equal = testA.Inputs.Count == testB.Inputs.Count
&& listASorted.Zip(listBSorted, (x, y) => x == y).All(b => b);
(If the lists are already sorted or if you'd prefer to check the lists exactly (with ordering preserved), then you can skip the sorting step of this method.)
One thing to note with this method, however, is that Input needs to implement IComparable in order for them to be properly sorted. How you implement it exactly is up to you, but one possible way would be to sort Input based on the XOR of VariableA and VariableB:
public class Input : IComparable<Input>
{
...
public int Compare(Input other)
{
int a = this.VariableA ^ this.VariableB;
int b = other.VariableA ^ other.VariableB;
return a.Compare(b);
}
}
(In addition, Input should also override GetHashCode and Equals, as itsme86 describes in his answer.)
EDIT:
After being drawn back to this answer, I would now like to offer a much simpler solution:
var listASorted = testA.Inputs.OrderBy(x => x);
var listBSorted = testB.Inputs.OrderBy(x => x);
bool equal = listASorted.SequenceEqual(listBSorted);
(As before, you can skip the sorting step if the lists are already sorted or you want to compare them with their existing ordering intact.)
SequenceEqual uses the equality comparer for a particular type for determining equality. By default, this means checking that the values of all public properties are equal between two objects. If you want to implement a different approach, you can define an IEqualityComparer for Input:
public class InputComparer : IEqualityComparer<Input>
{
public bool Equals(Input a, Input b)
{
return a.variableA == b.variableA
&& a.variableB == b.variableB
&& ... and so on
}
public int GetHashCode(Input a)
{
return a.GetHashCode();
}
}
You can change your Input and Sancti class definitions to override Equals and GetHasCode. The following solution considers that 2 Inputs are equal when:
VariableA are equal and
VariableB are equal and
The Sancts List are equal, considering that the Sancti elements with the same Symbol must have the same Percentage to be equal
You may need to change this if your specifications are different:
public class Input
{
public int VariableA { get; set; }
public int VariableB { get; set; }
public List<Sancti> Sancts { get; set; }
public override bool Equals(object obj)
{
Input otherInput = obj as Input;
if (ReferenceEquals(otherInput, null))
return false;
if ((this.VariableA == otherInput.VariableA) &&
(this.VariableB == otherInput.VariableB) &&
this.Sancts.OrderBy(x=>x.Symbol).SequenceEqual(otherInput.Sancts.OrderBy(x => x.Symbol)))
return true;
else
{
return false;
}
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + VariableA.GetHashCode();
hash = hash * 23 + VariableB.GetHashCode();
hash = hash * 23 + Sancts.GetHashCode();
return hash;
}
}
}
public class Sancti
{
public string Symbol { get; set; }
public double Percentage { get; set; }
public override bool Equals(object obj)
{
Sancti otherInput = obj as Sancti;
if (ReferenceEquals(otherInput, null))
return false;
if ((this.Symbol == otherInput.Symbol) && (this.Percentage == otherInput.Percentage) )
return true;
else
{
return false;
}
}
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + Symbol.GetHashCode();
hash = hash * 23 + Percentage.GetHashCode();
return hash;
}
}
}
Doing this, you just have to do this to check if Inputs are equal:
test1.Inputs.SequenceEqual(test2.Inputs);

Checking list of objects for differences

I have the following two lists:
List<MyObject> oldList = new List<MyObject>();
oldList.Add(new MyObject { Id = 1, Name = "hello" });
oldList.Add(New MyObject { Id = 2, Name = "world" });
List<MyObject> newList = new List<MyObject>();
newList.Add(new MyObject { Id = 1, Name = "Hello" });
newList.Add(new MyObject { Id = 3, Name = "World" });
newList.Add(new MyObject { Id = 4, Name = "hello" });
I would like to write something to compare the two lists and return boolean true if the lists are different.
For example, the lists above are different in the following ways:
Id's don't exactly match
Counts don't match
In cases where Id's do match, the Name's don't exactly match (case-sensitive)
I have tried the following:
if (oldList != newList)
And:
if (!oldList.SequenceEqual(newList))
However, both produce inaccurate results. I understand I can create a class of type IEqualityComparer which implements a HashSet comparison; but I also read that it may not work for my case... Can someone shed any light on how to compare two objects to detect the types of changes I have specified?
As you've said, you only need to implement the correct IEquatable<MyObject> interface in your MyObject, or implement an IEqualityComparer<MyObject>
/// <summary>
/// Fully equatable MyObject
/// </summary>
public class MyObject : IEquatable<MyObject>
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
// obj is object, so we can use its == operator
if (obj == null)
{
return false;
}
MyObject other = obj as MyObject;
if (object.ReferenceEquals(other, null))
{
return false;
}
return this.InnerEquals(other);
}
public bool Equals(MyObject other)
{
if (object.ReferenceEquals(other, null))
{
return false;
}
return this.InnerEquals(other);
}
private bool InnerEquals(MyObject other)
{
// Here we know that other != null;
if (object.ReferenceEquals(this, other))
{
return true;
}
return this.Id == other.Id && this.Name == other.Name;
}
public override int GetHashCode()
{
unchecked
{
// From http://stackoverflow.com/a/263416/613130
int hash = 17;
hash = hash * 23 + this.Id.GetHashCode();
hash = hash * 23 + (this.Name != null ? this.Name.GetHashCode() : 0);
return hash;
}
}
}
and then you can use
if (!oldList.SequenceEqual(newList))
Note that this will compare element order! If you change element order, then the comparison will return false
Or you can use an "external" IEqualityComparer<MyObject>
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public static readonly MyObjectEqualityComparer Default = new MyObjectEqualityComparer();
protected MyObjectEqualityComparer()
{
}
public bool Equals(MyObject x, MyObject y)
{
if (object.ReferenceEquals(x, null))
{
return object.ReferenceEquals(y, null);
}
if (object.ReferenceEquals(y, null))
{
return false;
}
// Here we know that x != null && y != null;
if (object.ReferenceEquals(x, y))
{
return true;
}
return x.Id == y.Id && x.Name == y.Name;
}
public int GetHashCode(MyObject obj)
{
if (obj == null)
{
return 0;
}
unchecked
{
// From http://stackoverflow.com/a/263416/613130
int hash = 17;
hash = hash * 23 + obj.Id.GetHashCode();
hash = hash * 23 + (obj.Name != null ? obj.Name.GetHashCode() : 0);
return hash;
}
}
}
use it like
if (!oldList.SequenceEqual(newList, MyObjectEqualityComparer.Default))
Note that there are various schools of thought on how exactly to write equality comparers, and there is a little caveat: if you override the operator== you must be very very wary of not using it inside your comparator :-)
A classical example of wrong code, when the operator== is overloaded
public bool Equals(MyObject other)
{
// BOOOM!!! StackOverflowException!
// Equals will call operator== that will probably call
// Equals back! and so on and so on.
if (other == null)
{
return false;
}
return this.InnerEquals(other);
}
So it is better to do object.ReferenceEquals(something, someotherthing) that does reference comparison.
Then there is the problem of null handling with properties:
The hash of Name (a string) is written like this:
hash = hash * 23 + obj.Name != null ? obj.Name.GetHashCode() : 0
so that if obj.Name is null the code doesn't expode in a NullReferenceException. Automatically generated code for anonymous objects use another way:
hash = hash * 23 + EqualityComparer<string>.Default.GetHashCode(obj.Name);
The EqualityComparer<string>.Default is safe to use, even with null values.
For the Equals comparison of properties, the automatically generated code for anonymous objects uses another funny trick:
&& EqualityComparer<string>.Default.Equals(this.Name, obj.Name);
so it uses EqualityComparer<string>.Default.Equals, that then correctly uses the various methods/interfaces to compare objects.

UnitTesting List<T> of custom objects with List<S> of custom objects for equality

I'm writing some UnitTests for a parser and I'm stuck at comparing two List<T> where T is a class of my own, that contains another List<S>.
My UnitTest compares two lists and fails. The code in the UnitTest looks like this:
CollectionAssert.AreEqual(list1, list2, "failed");
I've written a test scenario that should clarify my question:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ComparerTest
{
class Program
{
static void Main(string[] args)
{
List<SimplifiedClass> persons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello", ValueB="Hello"},
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
}
}
};
List<SimplifiedClass> otherPersons = new List<SimplifiedClass>()
{
new SimplifiedClass()
{
FooBar = "Foo1",
Persons = new List<Person>()
{
new Person(){ ValueA = "Hello2", ValueB="Hello2"},
new Person(){ ValueA = "Hello", ValueB="Hello"},
}
}
};
// The goal is to ignore the order of both lists and their sub-lists.. just check if both lists contain the exact items (in the same amount). Basically ignore the order
// This is how I try to compare in my UnitTest:
//CollectionAssert.AreEqual(persons, otherPersons, "failed");
}
}
public class SimplifiedClass
{
public String FooBar { get; set; }
public List<Person> Persons { get; set; }
public override bool Equals(object obj)
{
if (obj == null) { return false;}
PersonComparer personComparer = new PersonComparer();
SimplifiedClass obj2 = (SimplifiedClass)obj;
return this.FooBar == obj2.FooBar && Enumerable.SequenceEqual(this.Persons, obj2.Persons, personComparer); // I think here is my problem
}
public override int GetHashCode()
{
return this.FooBar.GetHashCode() * 117 + this.Persons.GetHashCode();
}
}
public class Person
{
public String ValueA { get; set; }
public String ValueB { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
Person obj2 = (Person)obj;
return this.ValueA == obj2.ValueA && this.ValueB == obj2.ValueB;
}
public override int GetHashCode()
{
if (!String.IsNullOrEmpty(this.ValueA))
{
//return this.ValueA.GetHashCode() ^ this.ValueB.GetHashCode();
return this.ValueA.GetHashCode() * 117 + this.ValueB.GetHashCode();
}
else
{
return this.ValueB.GetHashCode();
}
}
}
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
}
The question is strongly related to C# Compare Lists with custom object but ignore order, but I can't find the difference, other than I wrap a list into another object and use the UnitTest one level above.
I've tried to use an IEqualityComparer:
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x != null)
{
return x.Equals(y);
}
else
{
return y == null;
}
}
public int GetHashCode(Person obj)
{
return obj.GetHashCode();
}
}
Afterwards I've tried to implement the ''IComparable'' interface thats allows the objects to be ordered. (Basically like this: https://stackoverflow.com/a/4188041/225808)
However, I don't think my object can be brought into a natural order. Therefore I consider this a hack, if I come up with random ways to sort my class.
public class Person : IComparable<Person>
public int CompareTo(Person other)
{
if (this.GetHashCode() > other.GetHashCode()) return -1;
if (this.GetHashCode() == other.GetHashCode()) return 0;
return 1;
}
I hope I've made no mistakes while simplifying my problem. I think the main problems are:
How can I allow my custom objects to be comparable and define the equality in SimplifiedClass, that relies on the comparision of subclasses (e.g. Person in a list, like List<Person>). I assume Enumerable.SequenceEqual should be replaced with something else, but I don't know with what.
Is CollectionAssert.AreEqual the correct method in my UnitTest?
Equals on a List<T> will only check reference equality between the lists themselves, it does not attempt to look at the items in the list. And as you said you don't want to use SequenceEqual because you don't care about the ordering. In that case you should use CollectionAssert.AreEquivalent, it acts just like Enumerable.SequenceEqual however it does not care about the order of the two collections.
For a more general method that can be used in code it will be a little more complicated, here is a re-implemented version of what Microsoft is doing in their assert method.
public static class Helpers
{
public static bool IsEquivalent(this ICollection source, ICollection target)
{
//These 4 checks are just "shortcuts" so we may be able to return early with a result
// without having to do all the work of comparing every member.
if (source == null != (target == null))
return false; //If one is null and one is not, return false immediately.
if (object.ReferenceEquals((object)source, (object)target) || source == null)
return true; //If both point to the same reference or both are null (We validated that both are true or both are false last if statement) return true;
if (source.Count != target.Count)
return false; //If the counts are different return false;
if (source.Count == 0)
return true; //If the count is 0 there is nothing to compare, return true. (We validated both counts are the same last if statement).
int nullCount1;
int nullCount2;
//Count up the duplicates we see of each element.
Dictionary<object, int> elementCounts1 = GetElementCounts(source, out nullCount1);
Dictionary<object, int> elementCounts2 = GetElementCounts(target, out nullCount2);
//It checks the total number of null items in the collection.
if (nullCount2 != nullCount1)
{
//The count of nulls was different, return false.
return false;
}
else
{
//Go through each key and check that the duplicate count is the same for
// both dictionaries.
foreach (object key in elementCounts1.Keys)
{
int sourceCount;
int targetCount;
elementCounts1.TryGetValue(key, out sourceCount);
elementCounts2.TryGetValue(key, out targetCount);
if (sourceCount != targetCount)
{
//Count of duplicates for a element where different, return false.
return false;
}
}
//All elements matched, return true.
return true;
}
}
//Builds the dictionary out of the collection, this may be re-writeable to a ".GroupBy(" but I did not take the time to do it.
private static Dictionary<object, int> GetElementCounts(ICollection collection, out int nullCount)
{
Dictionary<object, int> dictionary = new Dictionary<object, int>();
nullCount = 0;
foreach (object key in (IEnumerable)collection)
{
if (key == null)
{
++nullCount;
}
else
{
int num;
dictionary.TryGetValue(key, out num);
++num;
dictionary[key] = num;
}
}
return dictionary;
}
}
What it does is it makes a dictionary out of the two collections, counting the duplicates and storing it as the value. It then compares the two dictionaries to make sure that the duplicate count matches for both sides. This lets you know that {1, 2, 2, 3} and {1, 2, 3, 3} are not equal where Enumerable.Execpt would tell you that they where.

How to get a Distinct result using LINQ and C# using method syntax [duplicate]

This question already has answers here:
How can I maintain type when using LINQ .Select in C#?
(4 answers)
Closed 9 years ago.
The following code still does not return a DISTINCT result set. The equivalent SQL I am trying to accomplish is SELECT DISTINCT LEFT(Fac_Name, 6) AS ID, LEFT(Fac_Name, 3) AS Fac_Name
public List<Facility> GetFacilities() {
var facilities = new List<Facility>();
facilities = _facilityRepository.GetAll().ToList();
var facReturnList =
facilities.Where(x => x.Fac_Name = "Something")
.OrderBy(x => x.Fac_Name).ToList();
var facReturnList2 =
facReturnList.Select(x =>
new Facility { ID = x.Fac_Name.Substring(0, 6),
Fac_Name = x.Fac_Name.Substring(0, 3) })
.Distinct().ToList();
return facReturnList2;
}
The problem you have is that you're creating distinct reference values (which will return different hashcodes), even if the properties inside each reference are equal, the actual references themselves are distinct.
// fac1 and fac2 are the same reference, fac3 is a different reference.
var fac1 = new Facility { ID = "0", Fac_Name = "Hello" };
var fac2 = fac1;
var fac3 = new Facility { ID = "0", Fac_Name = "Hello" };
var facs = new List<Facility>() { fac1, fac2, fac3 };
foreach (var fac in facs.Distinct())
Console.WriteLine("Id: {0} | Name: {1}", fac.ID, fac.Fac_Name);
// OUTPUT
// Id: 0 | Name: Hello (NOTE: This is the value of fac1/fac2)
// Id: 0 | Name: Hello (This is the value of fac3)
To solve your dilemma, you should either:
Override the Object.GetHashCode() and the Object.Equals(Object) methods. Note that Distinct() ultimately uses the GetHashCode() to determine if something is distinct, but Equals(Object) and GetHashCode() should be overridden together.
Guidelines for Overloading Equals() and Operator ==
public class Facility
{
public string ID { get; set; }
public string Fac_Name { get; set; }
// This is just a rough example.
public override bool Equals(Object obj)
{
var fac = obj as Facility;
if (fac == null) return false;
if (Object.ReferenceEquals(this, fac)) return true;
return (this.ID == fac.ID) && (this.Fac_Name == fac.Fac_Name);
}
public override int GetHashCode()
{
var hash = 13;
if (!String.IsNullOrEmpty(this.ID))
hash ^= ID.GetHashCode();
if (!String.IsNullOrEmpty(this.Fac_Name))
hash ^= Fac_Name.GetHashCode();
return hash;
}
}
Provide a custom IEqualityComparer<T>.
public class FacilityEqualityComparer : IEqualityComparer<Facility>
{
public bool Equals(Facility x, Facility y)
{
return (x.ID == y.ID) && (x.Fac_Name == y.Fac_Name);
}
public int GetHashCode(Facility fac)
{
var hash = 13;
if (!String.IsNullOrEmpty(this.ID))
hash ^= ID.GetHashCode();
if (!String.IsNullOrEmpty(this.Fac_Name))
hash ^= Fac_Name.GetHashCode();
return hash;
}
}
var facReturnList2 =
facReturnList.Select(x =>
new Facility { ID = x.Fac_Name.Substring(0, 6),
Fac_Name = x.Fac_Name.Substring(0, 3) })
.Distinct(new FacilityEqualityComparer()).ToList();
Also, some other things to note:
You're naming does not follow guidelines. Don't use underscores in property names, and ID should be Id.
Whichever way you decide to go with, you should look into using String.Equals(...) and specify a StringComparison value. I just used == equality comparison on strings to keep the post short and readable.
So the problem is that the Enumerable.Distinct method uses the default equality comparer - which is comparing hash codes - so it will be a distinct list regardless of the properties values. Build an equality comparer for that type:
public class FacilityEqualityComparer : IEqualityComparer<Facility>
{
public bool Equals(Facility fac1, Facility fac2)
{
return fac1.ID.Equals(fac2.ID) && fac1.Fac_Name.Equals(fac2.Fac_Name);
}
public int GetHashCode(Facility fac)
{
string hCode = fac.ID + fac.Fac_Name;
return hCode.GetHashCode();
}
}
and then when you use it, call it like this:
var facReturnList2 =
facReturnList.Select(x =>
new Facility { ID = x.Fac_Name.Substring(0, 6),
Fac_Name = x.Fac_Name.Substring(0, 3) })
.Distinct(new FacilityEqualityComparer()).ToList();
return facReturnList2;
Distinct uses the default equality comparer to check for equality. This means it's looking for reference equality, which obviously won't be there in your case.
So you'll either need to use a custom IEqualityComparer (see the overload for Distinct(), or you can replicate the functionality of Distinct() with a GroupBy() and a First():
facReturnList.Select(x =>
new Facility { ID = x.Fac_Name.Substring(0, 6),
Fac_Name = x.Fac_Name.Substring(0, 3)
})
.GroupBy(x => new{x.ID, x.Fac_Name})
.Select(y => y.First())
.ToList();
You could also Override the Equals method in your Facility class:
public override bool Equals(System.Object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
Facility objAsFacility = obj as Facility;
return Equals(objAsFacility);
}
protected bool Equals(Facility other)
{
if (other.Fac_Name == this.Fac_Name)
return true;
else return false;
}
public override int GetHashCode()
{
return this.Fac_Name.GetHashCode();
//Or you might even want to this:
//return (this.ID + this.Fac_Name).GetHashCode();
}
I'd probably go with the overriding equality operator method.

Comparing two classes in LINQ - getting 'mismatches'

I have the following class:
public class DocumentCompare
{
public string Customer;
public string Filename;
public string Reference;
public DateTime? Date;
public override bool Equals(object obj)
{
if (obj == null)
return false;
DocumentCompare doc = obj as DocumentCompare;
if ((Object)doc == null)
return false;
return (doc.Customer == Customer) && (doc.Date == Date) && (doc.Filename == Filename) && (doc.Reference == Reference);
}
public bool Equals(DocumentCompare doc)
{
if ((object)doc == null)
return false;
return (doc.Customer == Customer) && (doc.Date == Date) && (doc.Filename == Filename) && (doc.Reference == Reference);
}
public override int GetHashCode()
{
return string.Format("{0}_{1}_{2}_{3}",Customer,Filename,Reference,(Date == null ? "" : Date.Value.ToString())).GetHashCode();
}
}
I will be retrieving 2 lists of this class - what I want to do is to compare the two, and get ones that don't exist in both. So if an item exists in x list but not in y, I want to perform an action for the items in this list. If an item exists in y list but not in x, I want to do a different action.
How would I do this? Using LINQ I guess!
EDIT: Performance is not much of an issue - this will only be run once
It sounds like you just want Except:
foreach (var newItem in firstList.Except(secondList))
{
...
}
As an aside:
That's not a terribly nice way of generating a hash code - search for other questions here.
Delegate from Equals(object) to Equals(DocumentCompare) to avoid repetitive logic
Mutable types aren't great candidates for equality comparisons (in particular, one you've used a value as a key in a dictionary, if you change the equality-sensitive components you won't be able to find the key again)
Even if you do want it to be mutable, properties are better for encapsulation than public fields
I would either seal the type or check whether the two objects are exactly the same type, as otherwise you could end up with asymmetric equality
here is the code:
var elementsMissingFromFirstList = firstList.Except(secondList).ToList();
var elementsMissingInSecondList = secondList.Except(firstList).ToList();
now you can perform your actions on these missing elements :)
You can use this method to compare objects of two different Lists. exmp: List and List x and y = DocumentCompare,
public static bool EqualsObject<T>(this T t1, T t2) where T : class
{
var p1 = t1.GetType().Fields();
var p2 = t2.GetType().Fields();
for (int j = 0; j < p1.Length; j++)
{
var x = p1[j].GetValue(t1, null);
var y = p2[j].GetValue(t2, null);
if (x == null && y == null)
continue;
if (x != null && y == null)
return false;
if (x == null)
return false;
if (!x.Equals(y))
{
return false;
}
}
return true;
}
This method will show the difference between these two lists.
public static List<T> DifferentObjects<T>(List<T> t, List<T> t2) where T : class
{
var diff = new List<T>();
if (t != null && t2 != null)
{
foreach (T t1 in t)
{
var state = false;
foreach (T t3 in t2.Where(t3 => EqualsObject(t1,t3)))
{
state = true;
}
if (!state)
{
diff.Add(t1);
}
}
}
return diff;
}
you can use code this way
var t = new List<DocumentCompare>();
var t2 = new List<DocumentCompare>();
t.Add(new DocumentCompare{Customer = "x"});
t.Add(new DocumentCompare{Customer = "y"});
t.Add(new DocumentCompare{Customer = "z"});
t2.Add(new DocumentCompare { Customer = "t" });
t2.Add(new DocumentCompare { Customer = "y" });
t2.Add(new DocumentCompare { Customer = "z" });
var list = DifferentObjects(t, t2);
var list2 = DifferentObjects(t2, t);
you used fields (Customer,FileName etc..) in your class, so that GetType().Fields(); is used in EqualsObject method. if you use property , you should use GetType().Properties(); in EqualsObject method.

Categories

Resources