Comparing two lists according to specific properties - c#

how can i compare 2 lists and have the not matching items but according to the specifics properties
public partial class Cable : StateObject
{
public int Id { get; set; }
public int CablePropertyId { get; set; }
public int Item { get; set; }
public int TagNo { get; set; }
public string GeneralFormat { get; set; }
public string EndString { get; set; }
public string CableRevision { get; set; }
}
I want to comparision accomplished accoring to the CablePropertyId,TagNo and CableRevision, if i use
var diffCables = sourceCables.Except(destinationCables).ToList();
the whole properties are compared to each other . how can i do that?

Use Linq except method with custom EqualityComparer.
http://msdn.microsoft.com/en-us/library/bb336390(v=vs.110).aspx
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return (x.CablePropertyId == y.CablePropertyId && ...);
}
public int GetHashCode(Cable x) // If you won't create a valid GetHashCode based on values you compare on, Linq won't work properly
{
unchecked
{
int hash = 17;
hash = hash * 23 + x.CablePropertyID;
hash = hash * 23 + ...
}
return hash;
}
}
var diffCables = sourceCables.Except(destinationCables, new CableComparer());
Also, ToList() operation on the result isn't really necessary. Most of the time you can just operate on the result of Linq query IEnumerable without specifying the exact type; this way you won't waste performance on unneeded ToList() operation.
By the way, a couple of others proposed Where-based queries with simple lambda. Such solution is easier to read (in my opinion), but it's also less optimized: it forces n^2 checks, while IEqualityComparer allows Linq to be more optimal because of GetHashCode() method. Here's a great answer on importance of GetHashCode, and here's a great guide on writing GetHashCode() override.

You can create your own IEqualityComparer<Cable> like this:
public class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable x, Cable y)
{
return x.CablePropertyId == y.CablePropertyId &&
x.TagNo == y.TagNo &&
x.CableRevision == y.CableRevision;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Cable x)
{
return x.CablePropertyId ^
x.TagNo.GetHashCode() ^
x.CableRevision.GetHashCode();
}
}
Then use this overload of Except:
var comparer = new CableComparer();
var diffCables = sourceCables.Except(destinationCables, comparer).ToList();
Alternatively, the MoreLINQ library (also available on NuGet) provides a convenient ExceptBy method:
var diffCables = sourceCables.ExceptBy(
destinationCables,
x => new {
x.CablePropertyId,
x.TagNo,
x.CableRevision
})
.ToList();

You can override the Equals and GetHashCode methods of Cable if you will always compare this object in this manner.
Otherwise you can write a custom comparer and use the overload for .Except
List.Except Method

I think you can use something like this:
sourceCables.Where(sc => !destinationCables.Any(dc => dc.CablePropertyId == sc.CablePropertyId && ...));

Essentially, when you want to compare your own types, you'll need to describe how they compare/differ from each other. Linq wouldn't know which properties in your Cable class are different right?
So you build a comparer which can be used generally to compare two types.
In this case, two Cable instances:
class CableComparer : IEqualityComparer<Cable>
{
public bool Equals(Cable c1, Cable c2)//these represent any two cables.
{
if (c1.Height == c2.Height && ...)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Cable c)
{
//this will work if each ID is unique
return c.Id.GetHashCode();
//otherwise you do this:
//return (c.Id ^ c. CablePropertyId).GetHashCode();
}
}
Then:
IEnumerable<Cable> except =
sourceCables.Except(destinationCables, new CableComparer());

If you use LINQ with IQueryable<>, there may be solution with Where()
var destinationCablesAnon = destinationCables.Select(a=>new {a.CablePropertyId, a.TagNo ,a.CableRevision}); // add ToArray() if use IEnumerable<>
var diffCables = sourceCables.Where(a=>!destinationCables.Contains(new {a.CablePropertyId, a.TagNo ,a.CableRevision})).ToList();

Related

How to implement multiple GetHashCode methods?

I have an interface which defines a composite key:
public interface IKey : IEquatable<IKey>
{
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
int GetHashCode(); // never gets called
}
I have an object (with an ID) to which I want to add the composite key interface:
public class MyObject: IEquatable<MyObject>, IKey
{
public MyObject(int i, (bool keyPart1, uint keyPart2) key) {
{
Id=i;
KeyPart1 = key.keyPart1;
KeyPart2 = key.keyPart2;
}
public int Id { get; }
public bool KeyPart1 { get; }
public uint KeyPart2 { get; }
public bool Equals(MyObject other) => this.Id == other.Id;
public override bool Equals(object other) => other is MyObject o && Equals(o);
public override int GetHashCode() => Id.GetHashCode();
bool IEquatable<IKey>.Equals(IKey other) => this.KeyPart1 == other.KeyPart1
&& this.KeyPart2 == other.KeyPart2;
int IKey.GetHashCode() => (KeyPart1, KeyPart2).GetHashCode(); // never gets called
}
However, when have a list of these objects and try to group them using the interface, the grouping fails:
var one = new MyObject(1, (true, 1));
var two = new MyObject(2, (true, 1));
var three = new MyObject(1, (false, 0));
var items = new[] { one, two, three };
var byId = items.GroupBy(i => i);
// result: { [one, three] }, { [two] } -- as expected
var byKey = items.GroupBy<MyObject, IKey>(i => i as IKey);
// result: { [one, two, three] } // not grouped (by 'id' or 'key')
// expected: { [one, two] }, { [three] }
I'd expected that byId would have the items grouped by the Id property, and byKey would have the items grouped by the Key property.
However, byKey is not grouped at all. It appears that the override GetHashCode() method is always used rather than the explicitly implemented interface method.
Is it possible to implement something like this, where the type of the item being grouped determines the hash method to use (avoiding an EqualityComparer)?
I noticed this problem when passing the cast objects to another method expecting an IEnumerable<IKey>. I have a few different types implementing IKey and those with an existing GetHashCode() method did not work, while the others did.
Please note the objects have been simplified here and that I cannot easily change the interfaces (e.g. to use ValueTuple instead).
The GetHashCode() used in equality is either:
the one defined via object.GetHashCode(), if no equality comparer is provided
IEqualityComparer<T>.GetHashCode(T), if an equality comparer is provided
Adding your own GetHashCode() method on your own interface does nothing, and it will never be used, as it is not part of an API that the framework/library code knows about.
So, I'd forget about IKey.GetHashCode(), and either (or both):
make MyObject.GetHashCode() provide the functionality you need, or
provide a custom equality comparer separately to the MyObject instance
There are overloads of GroupBy that accept an IEqualityComparer<TKey>, for the second option.

Setting up a simple iequatable class c#

Cant find a simple answer. My problem is I am trying to compare the VALUE of an object in a list to the VALUE of an object...
my class:
public class MatchList
{
public int SomeInt { get; set; }
public decimal SomeDecimal { get; set; }
}
I create theMatchList. It seems that I can only compare the object and not the values for object with 'theMatchList.Contains...'
MatchList ML = new MatchList();
ML.SomeInt = 12;
ML.SomeDecimal = 2.3;
if (theMatchlist.Contains(ML))
{
DoSomething;
}
How do get to fire 'DoSomething'? Assuming that there is an entry in 'theMatchList' where the values equal 12 and 2.3 respectively. I know it has something to do with iequatable, but I dont quite understand how that works. Thanks in advance!
Your naming is a bit unclear, I assume that you actually have a List<MatchList> that you want to find a particular MatchList in (I suggest renaming MatchList to at least MatchItem in that case and preferable something more descriptive).
Then from the documentation of List<T>.Contains:
This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable<T>.Equals method for T (the type of values in the list).
So you will have to implement IEquatable<T> for your class. In addition, the advice is that
[i]f you implement Equals, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable.Equals method.
If you implement GetHashCode, its result should not change over the lifetime of your object. In most cases, making the class immutable is sufficient. If you need to be able to update the fields, you need to implement GetHashCode differently.
So all in all, if you want to use Contains your class will end up looking something like below:
public class MatchList : IEquatable<MatchList>
{
// Note: Fields are readonly to satisfy GetHashCode contract
private readonly int someInt;
private readonly decimal someDecimal;
// Public constructor creates immutable object
public MatchList(int myInt, decimal myDecimal)
{
this.someInt = myInt;
this.myDecimal = myDecimal;
}
// Properties are now read-only too.
public int SomeInt { get { return this.someInt; } }
public decimal SomeDecimal { get { return this.someDecimal; } }
// Implementation of IEquatable<MatchList>
public bool Equals( MatchList other )
{
return (other != null)
&& (this.SomeInt == other.SomeInt)
&& (this.SomeDecimal == other.SomeDecimal);
}
// Override of Object.Equals
// Calls the IEquatable.Equals version if possible.
public override bool Equals( object obj )
{
return (obj is MatchList) && this.Equals(obj as MatchList);
}
public override int GetHashCode()
{
return (this.someInt * 17) ^ this.someDecimal.GetHashCode();
}
}
As I commented, your question is pretty unclear so I'll do my best to explain the concept.
It's pretty likely what you were trying to code is the items in the list not the list itself:
public class MatchItem : IEquatable<MatchItem>
{
public int SomeInt { get; set; }
public decimal SomeDecimal {get; set; }
public bool Equals(MatchItem item)
{
if(item == null)
return false;
return this.SomeInt == item.SomeInt && this.SomeDecimal == item.SomeDecimal;
}
// You should also override object.ToString, object.Equals & object.GetHashCode.
// Omitted for brevity here!
}
You'll note that has an implementation of IEquatable<MatchItem> which allows it to be compared to other instances of MatchItem.
Thereafter, this code will work:
var items = new List<MatchItem>()
{
new MatchItem{SomeInt = 1, SomeDecimal = 0.3M},
new MatchItem{SomeInt = 12, SomeDecimal = 2.3M}
};
var searchItem = new MatchItem{SomeInt = 1, SomeDecimal = 0.3M};
Console.WriteLine(items.Contains(searchItem)); // true
Working example: http://rextester.com/ZWNC6890

Removing duplicates from a list<int,int> [duplicate]

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

Comparing/Equals two IList<T> objects

EDIT:
What I'm trying to do is to find if db.Id is equal to xml.Id and db.SubTitle is equal to xml.SubTitle ....etc.....all my prop
also I did tried
bool result = db.SequenceEqual(xml) it returns false all the time.
ENd EDIT
I did search before I end-up asking for help and I'm not sure what is the best way to approach to my problem.
I have two IList objects and both have exact same property but the data might be different.
one object is populating from db and other is reading from xml to compare both source is in sync.
here is my object looks like:
public class EmployeeObject
{
public Int32 Id { get; set; }
public string SubTitle { get; set; }
public string Desc { get; set; }
public bool Active { get; set; }
public string ActiveDateTime { get; set; }
}
here is what I have tried:
IList<EmployeeObject> db = Db.EmployeeRepository.PopulateFromDb();
IList<EmployeeObject> xml = Xml.EmployeeRepository.PopulateFromXml();
//both object populated with data so far so good....
Time to compare now:
I have tried some thing like this:
if ((object)xml == null || ((object)db) == null)
return Object.Equals(xml, db);
return xml.Equals(db); // returning false all the time
i have checked both object has the exact same data and but still returning false
The Equals method that you are using is going to determine if the two references refer to the same list, not if the contents are the same. You can use SequenceEqual to actually verify that two sequences have the same items in the same order.
Next you'll run into the issue that each item in the list will be compared to see if they refer to the same object, rather than containing the same field values, or the same ID values, as seems to be the what you want here. One option is a custom comparer, but another is to pull out the "identity" object in question:
bool areEqual = db.Select(item => item.id)
.SequenceEqual(xml.Select(item => item.id));
You should override Equals and GetHashCode in your class like this:
public class EmployeeObject {
public Int32 Id { get; set; }
public string SubTitle { get; set; }
public string Desc { get; set; }
public bool Active { get; set; }
public string ActiveDateTime { get; set; }
public override bool Equals(object o){
EmployeeObject e = o as EmployeeObject;
if(e == null) return false;
return Id == e.Id && SubTitle == e.SubTitle && Desc == e.Desc
&& Active == e.Active && ActiveDateTime == e.ActiveDateTime;
}
public override int GetHashCode(){
return Id.GetHashCode() ^ SubTitle.GetHashCode() ^ Desc.GetHashCode()
^ Active.GetHashCode() ^ ActiveDateTime.GetHashCode();
}
}
Then use the SequenceEqual method:
return db.OrderBy(e=>e.Id).SequenceEqual(xml.OrderBy(e=>e.Id));
IList does not have an Equals method. What you're calling is the standard Object equals which checks whether two variables point to the same object or not.
If you want to check that the lists are semantically equivalent, you will need to check that each object in the list is equivalent. If the EmployeeObject class has an appropriate Equals method, then you can use SequenceEquals to compare the lists.
You can implement an IEqualityComparer and use the overload of SequenceEquals that takes an IEqualityComparer. Here is sample code for an IEqualityComparer from msdn:
class BoxEqualityComparer : IEqualityComparer<Box>
{
public bool Equals(Box b1, Box b2)
{
if (b1.Height == b2.Height && b1.Length == b2.Length && b1.Width == b2.Width)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Box bx)
{
int hCode = bx.Height ^ bx.Length ^ bx.Width;
return hCode.GetHashCode();
}
}
You can then use SequenceEquals like this:
if (db.SequnceEquals(xml), new MyEqualityComparer())
{ /* Logic here */ }
Note that this will only return true if the items also are ordered in the same order in the lists. If appropriate, you can pre-order the items like this:
if (db.OrderBy(item => item.id).SequnceEquals(xml.OrderBy(item => item.id)), new MyEqualityComparer())
{ /* Logic here */ }
Obviously, the return of return xml.Equals(db); will always be false if you are comparing two different lists.
The only way for this to make sense is for you to actually be more specific about what it means for those two lists to be equal. That is you need to go through the elements in the two lists and ensure that the lists both contain the same items. Even that is ambiguous but assuming that the elements in your provide a proper override for Equals() and GetHashCode() then you can proceed to implement that actual list comparison.
Generally, the most efficient method to compare two lists that don't contain duplicates will be to use a hash set constructed from elements of one of the lists and then iterate through the elements of the second, testing whether each element is found in the hash set.
If the lists contain duplicates your best bet is going to be to sort them both and then walk the lists in tandem ensuring that the elements at each point match up.
You can use SequenceEqual provided you can actually compare instances of EmployeeObject. You probably have to Equals on EmployeeObject:
public override bool Equals(object o)
{
EmployeeObject obj = o as EmployeeObject;
if(obj == null) return false;
// Return true if all the properties match
return (Id == obj.Id &&
SubTitle == obj.SubTitle &&
Desc == obj.Desc &&
Active == obj.Active &&
ActiveDateTime == obj.ActiveDateTime);
}
Then you can do:
var same = db.SequenceEqual(xml);
You can also pass in a class that implements IEqualityComparer which instructs SequenceEqual how to compare each instance:
var same = db.SequenceEqual(xml, someComparer);
Another quick way, though not as fast, would be to build two enumerations of the value you want to compare, probably the id property in your case:
var ids1 = db.Select(i => i.Id); // List of all Ids in db
var ids2 = xml.Select(i => i.Id); // List of all Ids in xml
var same = ids1.SequenceEqual(ids2); // Both lists are the same

Mono implementation of Dictionary<T,T> using .Equals(obj o) instead of .GetHashCode()

By searching though msdn c# documentation and stack overflow, I get the clear impression that Dictionary<T,T> is supposed to use GetHashCode() for checking key-uniqueness and to do look-up.
The Dictionary generic class provides a mapping from a set of keys to a set of values. Each addition to the dictionary consists of a value and its associated key. Retrieving a value by using its key is very fast, close to O(1), because the Dictionary class is implemented as a hash table.
...
The speed of retrieval depends on the quality of the hashing algorithm of the type specified for TKey.
I Use mono (in Unity3D), and after getting some weird results in my work, I conducted this experiment:
public class DictionaryTest
{
public static void TestKeyUniqueness()
{
//Test a dictionary of type1
Dictionary<KeyType1, string> dictionaryType1 = new Dictionary<KeyType1, string>();
dictionaryType1[new KeyType1(1)] = "Val1";
if(dictionaryType1.ContainsKey(new KeyType1(1)))
{
Debug.Log ("Key in dicType1 was already present"); //This line does NOT print
}
//Test a dictionary of type1
Dictionary<KeyType2, string> dictionaryType2 = new Dictionary<KeyType2, string>();
dictionaryType2[new KeyType2(1)] = "Val1";
if(dictionaryType2.ContainsKey(new KeyType2(1)))
{
Debug.Log ("Key in dicType2 was already present"); // Only this line prints
}
}
}
//This type implements only GetHashCode()
public class KeyType1
{
private int var1;
public KeyType1(int v1)
{
var1 = v1;
}
public override int GetHashCode ()
{
return var1;
}
}
//This type implements both GetHashCode() and Equals(obj), where Equals uses the hashcode.
public class KeyType2
{
private int var1;
public KeyType2(int v1)
{
var1 = v1;
}
public override int GetHashCode ()
{
return var1;
}
public override bool Equals (object obj)
{
return GetHashCode() == obj.GetHashCode();
}
}
Only the when using type KeyType2 are the keys considered equal. To me this demonstrates that Dictionary uses Equals(obj) - and not GetHashCode().
Can someone reproduce this, and help me interpret the meaning is? Is it an incorrect implementation in mono? Or have I misunderstood something.
i get the clear impression that Dictionary is supposed to use
.GetHashCode() for checking key-uniqueness
What made you think that? GetHashCode doesn't return unique values.
And MSDN clearly says:
Dictionary requires an equality implementation to
determine whether keys are equal. You can specify an implementation of
the IEqualityComparer generic interface by using a constructor that
accepts a comparer parameter; if you do not specify an implementation,
the default generic equality comparer EqualityComparer.Default is
used. If type TKey implements the System.IEquatable generic
interface, the default equality comparer uses that implementation.
Doing this:
public override bool Equals (object obj)
{
return GetHashCode() == obj.GetHashCode();
}
is wrong in the general case because you might end up with KeyType2 instances that are equal to StringBuilder, SomeOtherClass, AnythingYouCanImagine and what not instances.
You should totally do it like so:
public override bool Equals (object obj)
{
if (obj is KeyType2) {
return (obj as KeyType2).var1 == this.var1;
} else
return false;
}
When you are trying to override Equals and inherently GetHashCode you must ensure the following points (given the class MyObject) in this order (you were doing it the other way around):
1) When are 2 instances of MyObject equal ? Say you have:
public class MyObject {
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public DateTime TimeWhenIBroughtThisInstanceFromTheDatabase { get; set; }
}
And you have 1 record in some database that you need to be mapped to an instance of this class.
And you make the convention that the time you read the record from the database will be stored
in the TimeWhenIBroughtThisInstanceFromTheDatabase:
MyObject obj1 = DbHelper.ReadFromDatabase( ...some params...);
// you do that at 14:05 and thusly the TimeWhenIBroughtThisInstanceFromTheDatabase
// will be assigned accordingly
// later.. at 14:07 you read the same record into a different instance of MyClass
MyObject obj2 = DbHelper.ReadFromDatabase( ...some params...);
// (the same)
// At 14:09 you ask yourself if the 2 instances are the same
bool theyAre = obj1.Equals(obj2)
Do you want the result to be true ? I would say you do.
Therefore the overriding of Equals should like so:
public class MyObject {
...
public override bool Equals(object obj) {
if (obj is MyObject) {
var that = obj as MyObject;
return (this.Name == that.Name) &&
(this.Address == that.Address) &&
(this.Age == that.Age);
// without the syntactically possible but logically challenged:
// && (this.TimeWhenIBroughtThisInstanceFromTheDatabase ==
// that.TimeWhenIBroughtThisInstanceFromTheDatabase)
} else
return false;
}
...
}
2) ENSURE THAT whenever 2 instances are equal (as indicated by the Equals method you implement)
their GetHashCode results will be identitcal.
int hash1 = obj1.GetHashCode();
int hash2 = obj2.GetHashCode();
bool theseMustBeAlso = hash1 == hash2;
The easiest way to do that is (in the sample scenario):
public class MyObject {
...
public override int GetHashCode() {
int result;
result = ((this.Name != null) ? this.Name.GetHashCode() : 0) ^
((this.Address != null) ? this.Address.GetHashCode() : 0) ^
this.Age.GetHashCode();
// without the syntactically possible but logically challenged:
// ^ this.TimeWhenIBroughtThisInstanceFromTheDatabase.GetHashCode()
}
...
}
Note that:
- Strings can be null and that .GetHashCode() might fail with NullReferenceException.
- I used ^ (XOR). You can use whatever you want as long as the golden rule (number 2) is respected.
- x ^ 0 == x (for whatever x)

Categories

Resources