How to compare two lists of objects? - c#

I have a simple class in C#:
public class Customer
{
public string CustomerName { get; set; }
public int CustomerId { get; set; }
public int RegionId { get; set; }
}
The Customers are stored in a List CustomersA and additionally i have a second with List CustomersB. CustomersB is the "Truth".
I need to find out
customers in CustomersB but not in CustomersA (by CustomerID)
customers in CustomersA but not in CustomersB (by CustomerID) (the same as above)
customers in CustomersB also with the same CustomerID in CustomerA but different CustomerName
How could i achieve this with C#?

You should use LINQ for set operations on data:
•customers in CustomersB but not in CustomersA (by CustomerID)
var query = customersB.Where(cB => !customersA.Any(cA => cA.Id == cB.Id)).ToList();
•customers in CustomersA but not in CustomersB (by CustomerID)
var query = customersA.Where(cA => !customersB.Any(cB => cB.Id == cA.Id)).ToList();
•customers in CustomersB also with the same CustomerID in CustomerA but different CustomerName
var query = customersB.Where(cB => customersA.Any(cA => cA.Id == cB.Id
&& cA.CustomerName != cB.CustomerName))
.ToList();

If you're planning on doing these comparisons in multiple places, you could also create a custom EqualityComparer<Customer> and re-use them wherever needed.
This is an example of two comparers, one based on Id and another based on Id and Name. I'm going to assume a customer id is unique in this example:
public sealed class CustomerByIdEqualityComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.CustomerId == y.CustomerId;
}
public int GetHashCode(Customer obj)
{
return obj.CustomerId;
}
}
public sealed class CustomerByIdAndNameEqualityComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return !string.Equals(x.CustomerName, y.CustomerName) &&
x.CustomerId == y.CustomerId;
}
public int GetHashCode(Customer obj)
{
return obj.CustomerId;
}
}
And you consume them using Enumerable.Except:
var comparerById = new CustomerByIdEqualityComparer();
var comparerByIdAndName = new CustomerByIdAndNameEqualityComparer();
var customerARelativeComplement = customersA.Except(customersB, comparerById);
var customerBRelativeComplement = customersB.Except(customersA, comparerById);
var customersBRelativeComplementByName = customersB
.Except(customersA, comparerByIdAndName);
If you're working with large sets, consider using HashSet<T> to do the filtering. Only problem with it is that you can only pass a single EqualityComparer<T> to it and you'll be bound to it.

Related

How to find not matching records in Generic List in C# [duplicate]

This question already has answers here:
Using Linq Except not Working as I Thought
(5 answers)
Closed 4 years ago.
I have two Generic List object, I want to get records which are not matching in second Generic list object. Below is my code. But it returning all records.
I want to ignore matching record in first list.
public class CuratedIncludeUid
{
public string type { get; set; }
public string uid { get; set; }
}
List<CuratedIncludeUid> newUids = new List<CuratedIncludeUid>();
newUids.Add(new CuratedIncludeUid { type = "series", uid = "600" });
List<CuratedIncludeUid> liExistingUids = new List<CuratedIncludeUid>();
liExistingUids.Add(new CuratedIncludeUid { type = "series", uid = "600" });
liExistingUids.Add(new CuratedIncludeUid { type = "series", uid = "200" });
var ied = liExistingUids.Except(newUids).ToList(); ;
foreach (var row in ied)
{
Console.WriteLine("Uid:" + row.uid + "type:" + row.type);
}
Console.Read();
I am getting Output as below
Uid:600type:series
Uid:200type:series
**My expected output as below
Uid:200type:series**
Either override Equals and GetHashCode
public class CuratedIncludeUid
{
public string type { get; set; }
public string uid { get; set; }
protected bool Equals(CuratedIncludeUid other)
{
return string.Equals(type, other.type) && string.Equals(uid, other.uid);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals((CuratedIncludeUid) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((type != null ? type.GetHashCode() : 0) * 397) ^ (uid != null ? uid.GetHashCode() : 0);
}
}
}
Or passing an IEqualityComparer to Except
public class CuratedIncludeUidEqualityComparer : IEqualityComparer<CuratedIncludeUid>
{
public bool Equals(CuratedIncludeUid x, CuratedIncludeUid y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false;
return string.Equals(x.type, y.type) && string.Equals(x.uid, y.uid);
}
public int GetHashCode(CuratedIncludeUid obj)
{
unchecked
{
return ((obj.type != null ? obj.type.GetHashCode() : 0) * 397) ^ (obj.uid != null ? obj.uid.GetHashCode() : 0);
}
}
}
var ied = liExistingUids.Except(newUids, new CuratedIncludeUidEqualityComparer()).ToList();
Either, you can implement Equals and GetHashCode or IEqualityComparer, or you can also do the following:
With All:
var ied = liExistingUids.Except(newUids).ToList();
liExistingUids
.Where(x => newUids.All(y => y.type != x.type && y.series != x.series))
.ToList();
With Any:
liExistingUids
.Where(x => !newUids.Any(y => y.type == x.type && y.series == x.series))
.ToList();

How can i return a list with 1 property not being duplicated?

I have a method that pulls a list of states and cities from a database. The states are unique, but there can be many cities in that state. What my method currently does is return each state, and city pair as an individual item. What I need it to do is have a state with many cities.
Currently Returns
oh- cincinnati
oh- cleveland
oh- findlay
in- indianapolis
what I need it to Return
oh -cincinnati,cleveland,findlay
in- indianapolis
MODEL
public class Location
{
public string State { get; set; }
public string city { get; set; }
}
REPOSITORY
public HashSet<Location> getlocation()
{
HashSet<Location> myHashset = new HashSet<Location>();
const string storedProc = "someProc";
dynamic locations;
using (var conn = DbFactory.myConnection())
{
locations = conn.Query(storedProc, commandType: CommandType.StoredProcedure);
}
foreach (var location in locations)
{
myHashset.Add(new location{State = location.state,City = location.city});
}
return myHashset
}
this should do it
var Result = myHashset.GroupBy(r => r.State)
.Select(g => new Location
{
State = g.Key,
city = String.Join(", ", g.Select(r => r.city))
});
maybe you don't want to store this into a new Location object. I'd use a Dictionary
Update - Dictionary
Dictionary<string,string> Result = myHashset.GroupBy(r => r.State)
.ToDictionary(g => g.Key, g => String.Join(", ", g.Select(r => r.city)));
[EDIT]: After reading your question again, this is probably not, what you are looking for. But if you want a HashSet which only differentiates by State (instead by State and City), then feel free to use this:
Quick and dirty:
Override the Location's equal and getHashcode methods:
public override bool Equals(Location other)
{
return this.State.Equals(other.State);
}
public override int GetHashCode()
{
return this.State.GetHashCode();
}
More clean:
Use an IEqualityComparer:
public class StateComparer : IEqualityComparer<Location>
{
public bool Equals(Location x, Location y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return Equals(x.State, y.State);
}
public int GetHashCode(Location obj)
{
if (ReferenceEquals(obj, null))
return 0;
if (ReferenceEquals(obj.State, null))
return 0;
return obj.State.GetHashCode();
}
}
Then create the HashSet with:
HashSet<Location> myHashset = new HashSet<Location>(new StateComparer());

Compare two custom LIST objects

I have scenario to check
1) if the any prop (EmployeeObject), from empDb appear in empXml , return true. Else return false
public class EmployeeObject
{
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
.....
}
IList<EmployeeObject> empDb = PopulateFromDb(); //calling ado.net
IList<EmployeeObject> empXml = PopulateFromXml(); //deserializing xml
So let me get this straight, I'm trying to determine if list empXml is a subset of list empDb ?
Tried so far; but it returns false even thought i have check the data in both list and it should have return true unless i am doing something wrong in my expression.
//at least one MATCH
empDb.Any(a => empXml.Contains(a));
or
//at least one EXACT match
empDb.Any(x => empXml.Contains(y => x.Equals(y)));
If you don't have Equals and GetHashCode implemented in EmployeeObject class then employees will be compared by reference. And you will definitely have different instances here, because first list is created when you read data from database, and second list is created when you are deserializing xml. So, even employees with same values of all fields will be considered different.
If you want to check matches only by employee Id, then you can project sequences to ids and then use Intersect to check if match exist
// at least one employee with equal Id
empDb.Select(e => e.Id).Intersect(empXml.Select(e => e.Id)).Any()
If you want to compare employees by value of their fields instead of their references, you have several options. If you can't or don't want to change implementation of EmployeeObject class and override its Equals and GetHashCode methods, then you can create custom comparer for employees:
public class EmployeeComparer : IEqualityComparer<EmployeeObject>
{
public bool Equals(EmployeeObject x, EmployeeObject y)
{
return x.Id == y.Id
&& x.Title == y.Title
&& x.Desc == y.Desc;
}
public int GetHashCode(EmployeeObject obj)
{
int code = 19;
code = code * 23 + obj.Id.GetHashCode();
code = code * 23 + obj.Title.GetHashCode();
code = code * 23 + obj.Desc.GetHashCode();
return code;
}
}
Then you can use this comparer:
empDb.Intersect(empXml, new EmployeeComparer()).Any()
Or you can project your employees to anonymous objects (which have default implementation of Equals and GetHashCode):
empDb.Select(e => new { e.Id, e.Title, e.Desc })
.Intersect(empXml.Select(e => new { e.Id, e.Title, e.Desc })).Any()
Or override these methods:
public class EmployeeObject
{
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
public override int GetHashCode()
{
int code = 19;
code = code * 23 + Id.GetHashCode();
code = code * 23 + Title.GetHashCode();
code = code * 23 + Desc.GetHashCode();
return code;
}
public override bool Equals(object obj)
{
EmployeeObject other = obj as EmployeeObject;
if (other == null)
return false;
if (ReferenceEquals(this, other))
return true;
return Id == other.Id &&
Title == other.Title && Desc == other.Desc;
}
}
And your code will work. Or you can use Intersect:
empDb.Intersect(empXml).Any()
If Id is the primary key of the entity you might want to write:
var set = new HashSet<int>(empXml.Select(x => x.Id)); //For faster lookup
empDb.Any(a => set.Contains(a.Id));
But if you need to match on all properties you need to override Equals and GetHashCode. (this implementation also match on null values for the properties)
public class EmployeeObject : IEquatable<EmployeeObject>
{
public bool Equals(EmployeeObject other)
{
return Id == other.Id &&
string.Equals(Title, other.Title) &&
string.Equals(Desc, other.Desc);
}
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((EmployeeObject) obj);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = Id;
hashCode = (hashCode*397) ^ (Title != null ? Title.GetHashCode() : 0);
hashCode = (hashCode*397) ^ (Desc != null ? Desc.GetHashCode() : 0);
return hashCode;
}
}
public Int32 Id { get; set; }
public string Title { get; set; }
public string Desc { get; set; }
}
And the write:
var set = new HashSet<EmployeeObject>(empXml); //For faster lookup
empDb.Any(a => set.Contains(a));

AssertFailedException in CollectionAssert.AreEquivalent (Both lists are filled with same content)

I get following AssertFailedException and can't see, why this happens.
CollectionAssert.AreEquivalent failed. The expected collection
contains 1 occurrence(s) of <FileExporter.Document>. The actual
collection contains 0 occurrence(s).
Code:
[TestMethod]
public void GetDocumentsTest()
{
List<Document> testDocuments = new List<Document>() {
new Document(){ Path = "TestDoc1", Language = "DEU" }
, new Document(){ Path = "TestDoc2", Language = "ENG" }
};
string sqlCommand = "SELECT Path, LanguageCode FROM Document WITH(NOLOCK) ORDER BY Id";
string connectionString = "Data Source=|DataDirectory|\\Documents.sdf;Persist Security Info=False;";
List<Document> documents = new DataAccessManagerTestMockup().GetDocuments(sqlCommand, connectionString);
// Sql gets same documents with same path and same language
CollectionAssert.AreEquivalent(testDocuments, documents);
}
Update:
namespace FileExporter
{
public class Document
{
public string Path { get; set; }
public string Language { get; set; }
public bool Equals(Document y)
{
if (object.ReferenceEquals(this, y))
return true;
if (object.ReferenceEquals(this, null) || object.ReferenceEquals(y, null))
return false;
return this.Path == y.Path && this.Language == y.Language;
}
}
}
Update 2:
I think i shouldn't spend a night in cologne (3 hours driving time) and try to code.
Colleague just said me i should override equals and use an object instead of Document.
namespace FileExporter
{
public class Document
{
public string Path { get; set; }
public string Language { get; set; }
public override bool Equals(object y)
{
if (object.ReferenceEquals(this, y))
return true;
if (object.ReferenceEquals(this, null) || object.ReferenceEquals(y, null))
return false;
return this.Path == ((Document) y).Path && this.Language == ((Document) y).Language;
}
public override int GetHashCode()
{
if (this == null)
return 0;
int hashCodePath = this.Path == null ? 0 : this.Path.GetHashCode();
int hashCodeLanguage = this.Language == null ? 0 : this.Language.GetHashCode();
return hashCodePath ^ hashCodeLanguage;
}
}
For this to succeed your Document class needs to implement (override) Equals()
You can verify this easily:
var d1 = new Document(){ Path = "TestDoc1", Language = "DEU" };
var d2 = new Document(){ Path = "TestDoc1", Language = "DEU" };
Assert.AreEqual(d1, d2);

MSUnit: Assert.AreEqual fails trees

I have to test the equality of trees. In other other words objects which contains List<T> with childs and the childs also contains List<T> with childs and so on.
I've found that you can test List with CollectionAssert, however it does not work that well with composites.
Any suggestions? MSUnit is my test library.
Example
IReagentComposed bronzeBarParsed = (from n in composedCrafts where n.ItemId == 2841 select n).Single();
IReagentComposed bronzeBar = new Craft()
{
ItemId = 2841,
Profession = Profession.Mining,
Quantity = 0,
QuantityCrafted = 0,
Skill = 50,
Reagents = new List()
{
new Craft()
{
ItemId = 2840,
Quantity = 0,
Skill = 1,
Profession = Profession.Mining,
Reagents = new List()
{
new Reagent()
{
ItemId = 2770,
Quantity = 1
}
}
},
new Craft()
{
ItemId = 3576,
Quantity = 0,
Skill = 50,
Profession = Profession.Mining,
Reagents = new List()
{
new Reagent()
{
ItemId = 2771,
Quantity = 1
}
}
}
}
};
Assert.AreEqual(bronzeBar, bronzeBarParsed);
Craft and Reagent
public class Craft : IReagentComposed
{
public int QuantityCrafted { get; set; }
public int Quantity { get; set;}
public int ItemId { get; set; }
public int Skill { get; set; }
public Profession Profession { get; set; }
public IEnumerable Reagents { get; set; }
public override bool Equals(object other)
{
if (other == null || GetType() != other.GetType()) return false;
IReagentComposed o = other as IReagentComposed;
return o != null && this.Quantity == o.Quantity &&
this.ItemId == o.ItemId &&
this.Profession == o.Profession &&
this.Reagents == o.Reagents && //also tried Equals
this.Skill == o.Skill;
}
public override int GetHashCode()
{
return 0;
}
}
public class Reagent : IReagent
{
public int ItemId { get; set; }
public int Quantity { get; set; }
public override bool Equals(object other)
{
if (other == null || GetType() != other.GetType()) return false;
IReagent o = other as IReagent;
return o != null && o.ItemId == this.ItemId && o.Quantity == this.Quantity;
}
public override int GetHashCode()
{
return 0;
}
}
return o != null && this.Quantity == o.Quantity &&
this.ItemId == o.ItemId &&
this.Profession == o.Profession &&
this.Reagents == o.Reagents && //also tried Equals
this.Skill == o.Skill;
The Reagents are unlikely to match, they are distinct List objects. List<> doesn't override Equals although it isn't that clear what actual type you use. Write a little helper function that takes two lists of reagents and checks for equality. You'd typically start at comparing List<>.Count and then work down the elements one by one.
Added a extension method for IEnumberable<T>:
public static class IEnumberableExtensions
{
public static bool AreEnumerablesEqual<T>(this IEnumerable<T> x, IEnumerable<T> y)
{
if (x.Count() != y.Count()) return false;
bool equals = false;
foreach (var a in x)
{
foreach (var b in y)
{
if (a.Equals(b))
{
equals = true;
break;
}
}
if (!equals)
{
return false;
}
equals = false;
}
return true;
}
}

Categories