I have an Entity Framework Entity that looks something like this:
class ListItemEtlObject
{
public int ID { get; set; }
public string ProjectName { get; set; }
public string ProjectType { get; set; }
public string ProjectCode { get; set; }
public string ProjectDescription { get; set; }
public string JobNo { get; set; }
public string JobDescription { get; set; }
public bool Include { get; set; }
}
I am pulling items from two different data sources into IEnumerable lists. How might I go about comparing the items without using a bunch of if statements to check if there are differences between the properties' values and then set the property's value if they do not match? The idea is to keep the lists synchronized. Also list A has an ID value set, list B does not. I just feel there is a better way to do this than a bunch of
if(objectA.ProjectName != objectB.ProjectName)
{
objectA.ProjectName = objectB.ProjectName;
}
If you have control of the source object then the best declarative way to support value based equality is to implement IEquatable<T>. This does unfortunately require you to enumerate out all of those checks but it's done once at the actual object definition location and doesn't need to be repeated throughout the code base.
class ListItemEtlObject : IEquatable<ListITemEtlObject>
{
...
public void Equals(ListITemEtlObject other) {
if (other == null) {
return false;
}
return
ID == other.ID &&
ProjectName == other.ProjectName &&
ProjectType == other.ProjectType &&
... ;
}
}
Additionally you could choose to overload the equality operator on the object type and allow consumers to simply use != and == on ListItemEtlObject instances and get value equality instead of reference equality.
public static bool operator==(ListItemEtlObject left, ListItemEtlObject right) {
return EqualityComparer<ListItemEtlObject>.Default.Equals(left, right);
}
public static bool operator!=(ListItemEtlObject left, ListItemEtlObject right) {
return !(left == right);
}
The easiest way would be to provide a method on your class that computes a specific hash, much like GetHashCode, and then if two instances compute the same hash, they can be said to be equivalent.
You could simplify it using reflection =)
public virtual void SetDifferences(MyBaseClass compareTo)
{
var differences = this.GetDifferentProperties(compareTo);
differences.ToList().ForEach(x =>
{
x.SetValue(this, x.GetValue(compareTo, null), null);
});
}
public virtual IEnumerable<PropertyInfo> GetDifferentProperties(MyBaseClass compareTo)
{
var signatureProperties = this.GetType().GetProperties();
return (from property in signatureProperties
let valueOfThisObject = property.GetValue(this, null)
let valueToCompareTo = property.GetValue(compareTo, null)
where valueOfThisObject != null || valueToCompareTo != null
where (valueOfThisObject == null ^ valueToCompareTo == null) || (!valueOfThisObject.Equals(valueToCompareTo))
select property);
}
And here are a couple of tests I did for you
[TestMethod]
public void CheckDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("Include"));
g.Include = true;
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
g.JobDescription = "my job";
f.GetDifferentProperties(g).Should().NotBeNull().And.HaveCount(1).And.Contain(f.GetType().GetProperty("JobDescription"));
}
[TestMethod]
public void SetDifferences()
{
var f = new OverridingGetHashCode();
var g = new OverridingGetHashCode();
g.Include = true;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include = true;
g.Include = false;
f.SetDifferences(g);
f.GetDifferentProperties(g).Should().NotBeNull().And.BeEmpty();
f.Include.Should().BeFalse();
}
Related
I have a MyCustomSet class with IEquatable implemented as shown below.
This works fantastic when I want to check equality for all three sets (SetA*, SetB*, and SetC*). But requirements dictate that I also need the ability to check equality for only SetA*, SetB*, or SetC*, or combination thereof (SetA* and SetB*, SetA* and SetC*, or SetB* and SetC*) and ignore checking equality for any other Set that is not required in the check.
Currently, I'm using foreach and LINQ to iterate through the Sets to perform partial equality checks, and that works, but that doesn't seem very efficient for large datasets.
Maybe the answer is looking at me straight in the face, but I don't see it because I don't have any idea how implement IEquatable that can handle different equality checks.
Would someone assist me with some suggestions or directions as to how this may be implemented? An example would be even more appreciated.
public static class HashCode
{
public const int Start = 17;
public static int Hash<T>(this int hash, T obj)
{
var h = EqualityComparer<T>.Default.GetHashCode(obj);
return unchecked((hash * 439) + h);
}
}
public class MyCustomSetModel
{
public sealed class MyCustomSet : IEquatable<MyCustomSet>
{
public string ItemName { get; set; }
public string ItemType { get; set; }
public double SetA1 { get; set; }
public double SetA2 { get; set; }
public double SetA3 { get; set; }
public double SetB1 { get; set; }
public double SetB2 { get; set; }
public double SetB3 { get; set; }
public double SetC1 { get; set; }
public double SetC2 { get; set; }
public double SetC3 { get; set; }
public bool Equals(MyCustomSet other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (ReferenceEquals(other, this))
{
return true;
}
return
(
(this.ItemName == other.ItemName) &&
(this.ItemType == other.ItemType) &&
(this.SetA1 == other.SetA1) &&
(this.SetA2 == other.SetA2) &&
(this.SetA3 == other.SetA3) &&
(this.SetB1 == other.SetB1) &&
(this.SetB2 == other.SetB2) &&
(this.SetB3 == other.SetB3) &&
(this.SetC1 == other.SetC1) &&
(this.SetC2 == other.SetC2) &&
(this.SetC3 == other.SetC3)
);
}
public override bool Equals(object obj) => Equals(obj as MyCustomSet);
public override int GetHashCode()
{
unchecked
{
return HashCode.Start
.Hash(ItemName)
.Hash(ItemType)
.Hash(SetA1)
.Hash(SetA2)
.Hash(SetA3)
.Hash(SetB1)
.Hash(SetB2)
.Hash(SetB3)
.Hash(SetC1)
.Hash(SetC2)
.Hash(SetC3);
}
}
}
}
* Update: *
Thanks to Matthew Watson who pointed me in the right direction. So I implemented a custom comparer as follows. It looks like it is working, but if anyone sees potential problems or room for better implementation, please feel free to comment.
public sealed class MyCustomSetComparer : IEqualityComparer<MyCustomSet>
{
private bool _compareSetA;
private bool _compareSetB;
private bool _compareSetC;
public MyCustomSetComparer(bool compareSetA = true, bool compareSetB = true, bool compareSetC = true)
{
_compareSetA = compareSetA;
_compareSetB = compareSetB;
_compareSetC = compareSetC;
}
public bool Equals(MyCustomSet x, MyCustomSet y)
{
if (Object.ReferenceEquals(x, y))
{
return true;
}
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
{
return false;
}
bool result =
(x.ItemName == y.ItemName) &&
(x.ItemType == y.ItemType);
if (_compareSetA)
{
result = result &&
(x.SetA1 == y.SetA1) &&
(x.SetA2 == y.SetA2) &&
(x.SetA3 == y.SetA3);
}
if (_compareSetB)
{
result = result &&
(x.SetB1 == y.SetB1) &&
(x.SetB2 == y.SetB2) &&
(x.SetB3 == y.SetB3);
}
if (_compareSetC)
{
result = result &&
(x.SetC1 == y.SetC1) &&
(x.SetC2 == y.SetC2) &&
(x.SetC3 == y.SetC3);
}
return result;
}
public int GetHashCode(MyCustomSet item)
{
if (Object.ReferenceEquals(item, null))
{
return 0;
}
int hash = HashCode.Start
.Hash(item.ItemName)
.Hash(item.ItemType);
if (_compareSetA)
{
hash = hash.Hash(item.SetA1)
.Hash(item.SetA2)
.Hash(item.SetA3);
}
if (_compareSetB)
{
hash = hash.Hash(item.SetB1)
.Hash(item.SetB2)
.Hash(item.SetB3);
}
if (_compareSetC)
{
hash = hash.Hash(item.SetC1)
.Hash(item.SetC2)
.Hash(item.SetC3);
}
unchecked
{
return hash;
}
}
}
As per Matthew Watson's comment, this is a perfect time to use IEqualityComparer<T> rather than IEquatable<T>. I would suggest only implementing IEquatable<T> when there's a single obvious, natural definition of equality for a type. When there are multiple options for equality and no single option is more reasonable than the others, implement IEqualityComparer<T> - either in several different implementation types, or in a single implementation type that is parameterized in terms of creation.
I have the following class and a HashSet of it:
public class VersionSettings {
public String Culture { get; set; }
public String Domain { get; set; }
public String Name { get; set; }
}
HashSet<VersionSetting> vs = ...
Questions
I would like two VersionSettings to be equal when Culture is the same.
In this moment it is not what happens. How can i solve this?
Would be possible to get a VersionSetting from the HashSet using:
var b = vs["en-US"];
Maybe using some HashSet extension?
You can override Equals and GetHashCode to not only compare references what Object.Equals does:
public class VersionSettings
{
public String Culture { get; set; }
public String Domain { get; set; }
public String Name { get; set; }
public override bool Equals(object obj)
{
VersionSettings other = obj as VersionSettings;
if(other == null) return false;
return Culture == other.Culture;
}
public override int GetHashCode()
{
return Culture == null ? 0 : Culture.GetHashCode();
}
}
Another approach is to implement a custom IEqualityComparer<VersionSettings> which does not require to modify the class itself:
public class VersionSettingComparer : IEqualityComparer<VersionSettings>
{
public bool Equals(VersionSettings x, VersionSettings y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.Culture == y.Culture;
}
public int GetHashCode(VersionSettings obj)
{
if(obj == null) return 0;
return obj.Culture == null ? 0 : obj.Culture.GetHashCode();
}
}
Now you can use the constructor of HashSet<T>:
var vs = new HashSet<VersionSettings>(new VersionSettingComparer());
According to your second question how you can access the HashSet<VersionSetting> in this way:
var enUsSetting = vs["en-US"];
That doesn't work because a hashset has no indexer like a collection or dictionary. You probably want a Dictionary<string, VersionSettings> instead.
As simple but not that efficient workaround you can use LINQ:
var enUsSetting = vs.FirstOrDefault(x => x.Culture == "en-US");
If you want to create the mentioned dictionary you can also use LINQ:
Dictionary<string, VersionSettings> cultureLoookup = vs
.ToDictionary(x => x.Culture, x => x); // you only need to create this once
Now this works:
var enUsSetting = cultureLoookup["en-US"];
Why in a simple way you doesn't use Dictionary<string, VersionSettings>. For instace:
var settings = new Dictionary<string, VersionSettings>();
settings["en-US"] = usSettings;
settings["es-ES"] = esSettings;
...
var currentSetting = settings[currentSettingIdText];
It is just a tip.
What would be the best (most elegant or performing) way of overloading the equality operator on a class containing only string attributes?
Example:
class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
}
I know how to overload the operator itself, however, I am wondering about the following points:
Is there a way to elegantly compare such two objects (e.g. without having to write an if statement containing mutual comparisons of all the attributes
What would be a good implementation of the GetHashCode() method in such case
How about something like this, Just create array of all properties and a loop.
internal class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
private string[] AllProperties//Array of all properties
{
get
{
return new[]
{
FirstAttribute,
SecondAttribute,
ThirdAttribute,
FourthAttribute,
FifthAttribute
};
}
}
protected bool Equals(MagicClass other)
{
var thisProps = this.AllProperties;
var otherProps = other.AllProperties;
return thisProps.SequenceEqual(otherProps);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MagicClass) obj);
}
public override int GetHashCode()
{
unchecked
{
var thisProps = this.AllProperties;
int hashCode = 0;
foreach (var prop in thisProps)
{
hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
}
return hashCode;
}
}
}
Then you can call Equals method inside your operator overload. If you're lazy to create AllProperties array you can use Reflection but IMO reflection is overkill here.
Not saying this is the 'best' or the most elegant solution, but I'd have the tendency to use an array and an index initializer, using an enumeration, so I could reuse get and set logic and in this case reset a hash code for a quick first comparison.
The advantage of the enumeration is, that you don't have to recheck your compare logic when an attribute is added, and you can prevent the overhead of resorting to reflection.
class MagicClass
{
string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];
public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
{
get
{
return Values[(int)Value];
}
set
{
Values[(int)Value] = value;
hash = null;
}
}
int? hash; //buffered for optimal dictionary performance and == comparisson
public override int GetHashCode()
{
if (hash == null)
unchecked
{
hash = Values.Sum(s => s.GetHashCode());
}
return hash.Value;
}
public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
{
if(ReferenceEquals(v1,v2))return true;
if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
return v1.Values.SequenceEqual(v2.Values);
}
public static bool operator !=(MagicClass v1, MagicClass v2)
{
return !(v1 == v2);
}
//optional, use hard named properties as well
public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}
public enum MagicClassValues
{
FirstAttribute,
SecondAttribute,
//etc
}
This is my object:
public class MyObject
{
public int id { get; set; }
public string fileName { get; set; }
public string browser { get; set; }
public string protocol { get; set; }
public string family { get; set; }
}
and i have a list of my object:
List<Capture> list = db.Captures.Where(x => x.family == "Web").ToList();
What i want to do is get new list that removed the duplicate protocol.
for example if i have in my list 10 object and 9 of them with protocol DOC and 1 PDF i want a new list with only 2 object DOC and 1 PDF
There are several ways to do this, depending on how you generally want to use the instances of your MyObject class.
The easiest one is implementing the IEquatable<T> interface so as to compare only the protocol fields:
public class MyObject : IEquatable<MyObject>
{
public sealed override bool Equals(object other)
{
return Equals(other as MyObject);
}
public bool Equals(MyObject other)
{
if (other == null) {
return false;
} else {
return this.protocol == other.protocol;
}
}
public override int GetHashCode()
{
return protocol.GetHashCode();
}
}
You can then call Distinct before converting your enumerable into a list.
Alternatively, you can use the Distinct overload that takes an IEqualityComparer.
The equality comparer would have to be an object that determines equality based on your criteria, in the case described in the question, by looking at the protocol field:
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
if (x == null) {
return y == null;
} else {
if (y == null) {
return false;
} else {
return x.protocol == y.protocol;
}
}
}
public int GetHashCode(MyObject obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
return obj.protocol.GetHashCode();
}
}
I believe this is the simplest approach: The following will group list by protocol and then get the first instance from each group to produce an enumerable with one instance of each type of protocol.
list.GroupBy(x => protocol, x => x)
.SelectMany(k, v => v.First());
You could either use Distinct, or use the same solution provided here:
Distinct() with lambda?
Select distinct protocols, loop on them and subselect only first object of the same protocol - thus you'll get the list you need.
Say I have an object Person with the properties below:
public class Person
{
public int ID { get; set; }
public int EmployeeNo { get; set; }
public string JobDescription { get; set; }
public string Code { get; set; }
}
How would I dynamically check the equality of specific properties by name?
eg.
var dynamicEqualityComparer = RetrieveDynamicEqualityComparer("ID", "JobDescription");
var intersectedPersons = listOfPerson1.Intersect(listOfPerson2, dynamicEqualityComparer);
The above snippit would use the default linq intersect method using the dynamically generated equality comparison method which only compares the fields "ID" and "JobDescription".
I would assume that something like this would have been easy to find, but so far have not been able to locate anything of the sort.
Put this in your person class then with your instance you can call equals
public override bool Equals(object obj)
{
return obj.ToString() == this.ToString();
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
public override string ToString()
{
string myState;
myState = string.Format("[ID: {0};EmployeeNo : {1}; JobDescription: {2};Code :{3}]",ID,EmployeeNo,JobDescription,Code);
return myState;
}
since the override of tostring accounts for all state data,in override equals you
simply leverage your own implementation of ToString().
public int Compare(Person x, Person y)
{
if (x.ID == y.ID && x.JobDescription == y.JobDescription)
return 0;
return (x.ID > y.ID) ? 1 : -1;//here you put what condition to return here i put ID just
//for clarity,if u want just return -1 for ex:
}
this is the implementation of the IComparer<> interface of type Person
The solution I came to is below:
The equality comparer class looks like:
public class CustomPropertyEqualityComparer<T>: IEqualityComparer<T> where T : class
{
private readonly string[] _selectedComparisonProperties;
public CustomPropertyEqualityComparer(params string[] selectedComparisonProperties)
{
_selectedComparisonProperties = selectedComparisonProperties;
}
public bool Equals(T x, T y)
{
if (x != null && y != null && x.GetType() == y.GetType())
{
var type = x.GetType();
var comparableProperties = new List<string>(_selectedComparisonProperties);
var objectProperties = type.GetProperties();
var relevantProperties = objectProperties.Where(propertyInfo => comparableProperties.Contains(propertyInfo.Name));
foreach (var propertyInfo in relevantProperties)
{
var xPropertyValue = type.GetProperty(propertyInfo.Name).GetValue(x, null);
var yPropertyValue = type.GetProperty(propertyInfo.Name).GetValue(y, null);
if (xPropertyValue != yPropertyValue && (xPropertyValue == null || !xPropertyValue.Equals(yPropertyValue)))
{
return false;
}
}
return true;
}
return x == y;
}
public int GetHashCode(T obj)
{
var type = typeof(T);
var objectProperties = type.GetProperties();
return objectProperties.Sum(property => property.GetHashCode());
}
}
To create this class, you pass in a list of strings representing the objects property names.
To call this class, I used the following bit of code:
var groupKey = new List<string> {"EmployeeNo", "ID"}.ToArray();
var customEqualityComparer = new CustomPropertyEqualityComparer<object>(groupKey);
This creates a custom equality comparer for any class with the properties "EmployeeNo" and "ID".
I used this comparer when checking if two tables contain the same entries where equality doesn't necessarily mean that every single field is equal..
var existInBothTables = table1.Intersect(table2, customEqualityComparer).ToList();