I have two Lists
ListA<Emp> and ListB<Emp>
both are having 1000 records.
Emp is an object of Employee Class. Below is my Employee class
public class Employee
{
int ID = 0;
string Name = String.Empty;
string Dept = String.Empty;
string Address = String.Empty;
int Age = 0;
string Email = String.Empty;
}
I want to verify if both the Lists are equal. The Emp objects may be placed in different order. Also, there might be several Emp objects which are having exactly same info in both the list. I have to verify those also.
I tried to sort the lists and compared using SequenceEqual
Enumerable.SequenceEqual(ListA.OrderBy(s => s), ListB.OrderBy(s => s)
I am getting below error
At least one object must implement IComparable.
Exception Stack trace is as below
at System.Collections.Comparer.Compare(Object a, Object b)
at System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
at System.Linq.EnumerableSorter`2.CompareKeys(Int32 index1, Int32 index2)
at System.Linq.EnumerableSorter`1.QuickSort(Int32[] map, Int32 left, Int32 right)
at System.Linq.EnumerableSorter`1.Sort(TElement[] elements, Int32 count)
at System.Linq.OrderedEnumerable`1.<GetEnumerator>d__0.MoveNext()
at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.SequenceEqual[TSource](IEnumerable`1 first, IEnumerable`1 second)
How can I implement this ? Also it will be better if you guys can provide me the fastest way of doing this because the number of objects in List may grow to 10 million.
Thanks for your help !
EDIT: Every employee must be in both list, order does not matter. But, if ListA contains same employee object 5 times (that means some duplicate entries), and ListB contains the employee object 4 times, then ListA and ListB are not equal.
You can use SequenceEqual with a custom IEqualityComparer<Employee>:
class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
if (x == null || y == null) return false;
bool equals = x.ID==y.ID && x.Name == y.Name && x.Dept == y.Dept
&& x.Address == y.Address && x.Age == y.Age && x.Email == y.Email;
return equals;
}
public int GetHashCode(Employee obj)
{
if (obj == null) return int.MinValue;
int hash = 19;
hash = hash + obj.ID.GetHashCode();
hash = hash + obj.Name.GetHashCode();
hash = hash + obj.Dept.GetHashCode();
hash = hash + obj.Address.GetHashCode();
hash = hash + obj.Age.GetHashCode();
hash = hash + obj.Email.GetHashCode();
return hash;
}
}
Now it's so simple:
listA.SequenceEqual(ListB, new EmployeeComparer());
If the order is not important and you only want to know if all employees are in both lists you can use HashSet<Employee>.SetEquals to determine if both lists contain the same people:
var empComparer = new EmployeeComparer();
bool bothEqual = new HashSet<Employee>(ListA, empComparer)
.SetEquals(new HashSet<Employee>(ListB, empComparer));
Best complexity is O(N)
Following realization with using HashSet:
Class with implementation of GetHashCode and Equals:
public class Employee
{
public int ID = 0;
public string Name = String.Empty;
public string Dept = String.Empty;
public string Address = String.Empty;
public int Age = 0;
public string Email = String.Empty;
public override int GetHashCode()
{
return
ID.GetHashCode() ^
(Name ?? String.Empty).GetHashCode() ^
(Dept ?? String.Empty).GetHashCode() ^
(Address ?? String.Empty).GetHashCode() ^
Age.GetHashCode() ^
(Email ?? String.Empty).GetHashCode()
;
}
public override bool Equals(object obj)
{
Employee other = obj as Employee;
if (obj == null)
return false;
return ID == other.ID &&
Name == other.Name &&
Dept == other.Dept &&
Address == other.Address &&
Age == other.Age &&
Email == other.Email;
}
}
Function to compare lists:
public static bool CompareLists(List<Employee> list1, List<Employee> list2)
{
if (list1 == null || list2 == null)
return list1 == list2;
if (list1.Count != list2.Count)
return false;
Dictionary<Employee, int> hash = new Dictionary<Employee, int>();
foreach (Employee employee in list1)
{
if (hash.ContainsKey(employee))
{
hash[employee]++;
}
else
{
hash.Add(employee, 1);
}
}
foreach (Employee employee in list2)
{
if (!hash.ContainsKey(employee) || hash[employee] == 0)
{
return false;
}
hash[employee]--;
}
return true;
}
If the numbers in the list are going to grow enormous (10M), you are probably going to have to consider parallelization of the look-up to get an acceptable query time.
Consider using PLINQ.
Some more clarity on what you mean by 'equal' would be good. How complex is the equivalence check? Are you checking that the objects are the same or that the objects values are the same?
Another consideration would be this; if the number of elements are going to become large, could you consider moving this check down from .NET into your database - perhaps as a stored procedure? You may find it executes more efficiently there.
reduce the list to a scalar type: int, string, ....
L1.Select(x => x.K).ToArray()
use the except method
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray())
If the count of the resulting set is 0 then the List are equals
L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count()
All together
public class Program {
public static void Main(String[] args) {
List<O> L1 = new List<O>{
new O {K = 1, V = "abcd"},
new O {K = 2, V = "efgh"}
};
List<O> L2 = new List<O>{
new O {K = 1, V = "abcd"}
};
List<O> L3 = new List<O>{
new O {K = 1, V = "abcd"},
new O {K = 3, V = "ijkl"}
};
List<O> L4 = new List<O>{
new O {K = 2, V = "efgh"},
new O {K = 1, V = "abcd"}
};
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L1.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L2.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L3.Select(x => x.K).ToArray()).Count());
Console.WriteLine(L1.Select(x => x.K).ToArray().Except(L4.Select(x => x.K).ToArray()).Count());
}
}
public class O {
public int K { get; set; }
public String V { get; set; }
}
Exactly what it says.
Implement IComparable on the class Employee
Also need to override Equals
Due to potentially a large number of calls to GetHashCode save it and only calculate on changes.
Tested
IComparable Interface
public MainWindow()
{
InitializeComponent();
List<Person> PLa = new List<Person>();
List<Person> PLb = new List<Person>();
PLa.Add(new Person { Age = 3, Name = "Jim"});
PLa.Add(new Person { Age = 2, Name = "Jimmmy" });
PLa.Add(new Person { Age = 1, Name = "Jim" });
PLb.Add(new Person { Age = 1, Name = "Jim" });
PLb.Add(new Person { Age = 3, Name = "Jim" });
PLb.Add(new Person { Age = 2, Name = "Jimmmy" });
System.Diagnostics.Debug.WriteLine(ListSameIgnoreOrder(PLa, PLb));
}
public bool ListSameIgnoreOrder(List<Person> PLa, List<Person> PLb)
{
if (PLa.Count != PLb.Count) return false;
//PLa.Sort();
//PLb.Sort();
return Enumerable.SequenceEqual(PLa.OrderBy(s => s), PLb.OrderBy(s => s));
//for (int i = 0; i < PLa.Count; i++)
//{
// System.Diagnostics.Debug.WriteLine(
// PLa[i].Age.ToString() + " " + PLb[i].Age.ToString() + " " +
// PLa[i].Name + " " + PLb[i].Name);
// if (!PLa[i].Equals(PLb[i])) return false;
//}
//return true;
}
public class Person : object, IComparable
{
private int age = 0;
private string name = string.Empty;
private int hash;
public int Age
{
get { return age; }
set
{
if (age == value) return;
age = value;
CalcHash();
}
}
public string Name
{
get { return name; }
set
{
if (name == value) return;
name = value;
CalcHash();
}
}
public override bool Equals(Object obj)
{
//Check for null and compare run-time types.
if (obj == null || !(obj is Person)) return false;
Person f = (Person)obj;
if (f.Age != this.Age) return false;
return (string.Compare(f.name, this.name) == 0);
}
private void CalcHash()
{
hash = Age.GetHashCode() ^
(Name ?? String.Empty).GetHashCode();
}
public override int GetHashCode()
{
return hash;
//return age ^ name.GetHashCode();
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
Person otherPerson = obj as Person;
if (otherPerson != null)
{
if (otherPerson.Age > this.Age) return -1;
if (otherPerson.Age < this.Age) return 1;
// compare all properties like above
return string.Compare(otherPerson.name, this.name);
}
else
throw new ArgumentException("Object is not a Person");
}
public Person() { CalcHash(); }
}
This works.
public bool EqualList(Dictionary<int, string> a, Dictionary<int, string> b)
{
if (a.Count == b.Count)
{
bool rs = false;
foreach (var i in a)
{
if (b.ContainsKey(i.Key))
{
rs = true;
}
else
{
rs = false;
break;
}
}
return rs;
}
else
{
return false;
}
Usage:
if(EqualList(List<A>.ToDictionary(k => k.Key, k => k.Value), List<B>.ToDictionary(k => k.Key, k => k.Value)){
}else{
}
Related
I have read about the IEqualityComparer interface. Here is my code (which says more then a thousand words)
static void Main(string[] args)
{
var Send = new ObservableCollection<ProdRow>() {
new ProdRow() { Code = "8718607000065", Quantity = 1 },
new ProdRow() { Code = "8718607000911", Quantity = 10 }
};
var WouldSend = new ObservableCollection<ProdRow>() {
new ProdRow() { Code = "8718607000065", Quantity = 1 },
new ProdRow() { Code = "8718607000072", Quantity = 1 },
new ProdRow() { Code = "8718607000256", Quantity = 1 },
new ProdRow() { Code = "8718607000485", Quantity = 1 },
new ProdRow() { Code = "8718607000737", Quantity = 1 },
new ProdRow() { Code = "8718607000911", Quantity = 20 }
};
//var sendToMuch = Send.Except(WouldSend).ToList();
//var sendToLittle = WouldSend.Except(Send).ToList();
//if (sendToMuch.Any() || sendToLittle.Any())
// var notGood = true;
//else
// var okay = true;
var sendToMuch = Send.ToList();
var sendToLittle = WouldSend.ToList();
foreach (var s in Send) {
var w = WouldSend.FirstOrDefault(d => d.Code.Equals(s.Code));
if (w != null) {
if (w.Quantity == s.Quantity) {
sendToMuch.Remove(s);
sendToLittle.Remove(w);
continue;
}
if (w.Quantity > s.Quantity) {
sendToLittle.Single(l => l.Code == w.Code).Quantity = (w.Quantity - s.Quantity);
sendToMuch.Remove(s);
} else {
sendToMuch.Single(l => l.Code == w.Code).Quantity = (s.Quantity - w.Quantity);
sendToLittle.Remove(s);
}
} else {
sendToMuch.Add(s);
}
}
}
The commented lines where what I would hoped that would work... the stuff below with what I ended up with.
As reference, here is my ProdRow class:
class ProdRow : INotifyPropertyChanged, IEqualityComparer<ProdRow>
{
private string _code;
private int _quantity;
public string Code {
get { return _code; }
set {
_code = value;
OnPropertyChanged("Code");
}
}
public int Quantity {
get { return _quantity; }
set {
_quantity = value;
OnPropertyChanged("Quantity");
}
}
private void OnPropertyChanged(string v) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(v));
}
public new bool Equals(object x, object y) {
if (((ProdRow)x).Code.Equals(((ProdRow)y).Code) && ((ProdRow)x).Quantity == ((ProdRow)y).Quantity)
return true;
else
return false;
}
public int GetHashCode(object obj) {
return obj.GetHashCode();
}
public bool Equals(ProdRow x, ProdRow y) {
if (x.Code.Equals(y.Code) && x.Quantity == y.Quantity)
return true;
else
return false;
}
public int GetHashCode(ProdRow obj) {
throw new NotImplementedException();
}
public event PropertyChangedEventHandler PropertyChanged;
}
I did not expected the commented part to work, because it cannot know to decrease the int of quantity etc. but I would like to know if there is a more efficient way to do this then the solution I used (below the commented lines). Perhaps flatten the collection like a string[]?
P.S. Sorry for the "PascalCase" of Send and WouldSend
IEqualityComparer<T> is not the right interface to implement for a class whose instances you wish to compare. IEqualityComparer<T> implementations are for creating objects that do comparisons from the outside of the objects being compared, which becomes important when you need to re-define what it means for two objects to be equal without access to the code of these objects, or when you need to use different semantic for equality depending on the context.
The right interface for strongly typed equality comparison is IEquatable<T>. However, in your case all you need is overriding Object's Equals(object) and GetHashCode():
public new bool Equals(object obj) {
if (obj == this) return true;
var other = obj as ProdRow;
if (other == null) return false;
return Code.Equals(other.Code) && Quantity == other.Quantity;
}
public int GetHashCode() {
return 31*Code.GetHashCode() + Quantity;
}
As far as computing quantities goes, you can do it with negative numbers and GroupBy:
var quantityByCode = WouldSend.Select(p => new {p.Code, p.Quantity})
.Concat(Send.Select(p => new {p.Code, Quantity = -p.Quantity}))
.GroupBy(p => p.Code)
.ToDictionary(g => g.Key, g => g.Sum(p => p.Quantity));
var tooLittle = quantityByCode
.Where(p => p.Value > 0)
.Select(p => new ProdRow {Code = p.Key, Quantity = p.Value})
.ToList();
var tooMuch = quantityByCode
.Where(p => p.Value < 0)
.Select(p => new ProdRow {Code = p.Key, Quantity = -p.Value})
.ToList();
I'm trying to set up a way to compare some nested lists with objects that I'm importing from MongoDB. I have already set up the lists object:
public class SecurityGroup
{
public ObjectId Id { get; set; }
public string GroupID { get; set; }
public string GroupName{ get; set; }
public List<IpPermission> IpPermissions { get; set; }
public override string ToString()
{
return string.Format("groupid : {0}, groupname : {1} ", GroupID, GroupName );
}
With in that class I also have an overridden Equals method in place.
public override bool Equals(object obj)
{
SecurityGroup secGroup = obj as SecurityGroup;
if (secGroup == null)
{
return false;
}
if (!string.Equals(GroupID, secGroup.GroupID, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!string.Equals(GroupName,secGroup.GroupName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
I'm not sure if I need to post the class used for the nested loop but there's an IEquatable interface and the entireity of this class is EXACTLY like the SecurityGroup class I just posted.
//Compare IpPermissions
var diff1 = IpPermissions.Except(secGroup.IpPermissions);
var diff2 = secGroup.IpPermissions.Except(IpPermissions);
if (diff1.Any() || diff2.Any())
{
return false;
}
return true;
}
Now here's the Hashcode method that I set up:
public override int GetHashCode()
{
unchecked
{
const int HashingBase = (int)2166136261;
const int HashingMultiplier = 16777619;
int hash = HashingBase;
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpPort) ? IpPort.GetHashCode() : 0);
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpProtocol) ? IpProtocol.GetHashCode() : 0);
hash = (hash * HashingMultiplier) ^ (!Object.ReferenceEquals(null, IpRanges) ? IpRanges.GetHashCode() : 0);
return hash;
}
}
}
}
That essentially conludes the code I have in place to set up the framework for my comparesList method. Now here's where I'm having issues. I'm trying to set up the compare lists method and it's just giving me underscored red lines on the 'public static' part. The thing I can't figure out is the error it's giving me for the 'foreach' statements. It's saying, "Cannot convert element type 'AwsInstanceProfile1.Entity.SecurityGroup' to iterator type 'Amazon.EC2.Model.SecurityGroup'" Which is really weird because I should in theory have the framework set up to allow the lists into these objects. Here's the rest of the method:
public static bool CompareLists(List<Entity.SecurityGroup> list1, List<Entity.SecurityGroup> list2) =>
{
if (list1 == null || list2 == null)
return list1 == list2;
Dictionary<SecurityGroup, int> hash = new Dictionary<SecurityGroup, int>();
foreach (SecurityGroup secGroup in list1)
{
if (hash.ContainsKey(secGroup))
{
hash[secGroup]++;
}
else
{
hash.Add(secGroup, 1);
}
}
foreach (SecurityGroup secGroup in list2)
{
if (!hash.ContainsKey(secGroup) || hash[secGroup] == 0)
{
return false;
}
hash[secGroup]--;
}
return true;
}
I have a custom class named as City and this class has an Equals method. The SequenceEqual method works good when comparing arrays with assigned variables. The problem occurs when comparing two arrays that contains the elements formatted new City(). It results as false.
City class:
interface IGene : IEquatable<IGene>
{
string Name { get; set; }
int Index { get; set; }
}
class City : IGene
{
string name;
int index;
public City(string name, int index)
{
this.name = name;
this.index = index;
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public int Index
{
get
{
return index;
}
set
{
index = value;
}
}
public bool Equals(IGene other)
{
if (other == null && this == null)
return true;
if((other is City))
{
City c = other as City;
return c.Name == this.Name && c.Index == this.Index;
}
return false;
}
}
In the Test method below, the first comparing result arrayCompare1 is true and the second result arrayCompare2 is false. Both compare result must be true but there is an anormal stuation. How can I fix this problem?
Test code:
public void Test()
{
City c1 = new City("A", 1);
City c2 = new City("B", 2);
City[] arr1 = new City[] { c1, c2 };
City[] arr2 = new City[] { c1, c2 };
City[] arr3 = new City[] { new City("A", 1), new City("B", 2) };
City[] arr4 = new City[] { new City("A", 1), new City("B", 2) };
bool arrayCompare1 = arr1.SequenceEqual(arr2);
bool arrayCompare2 = arr3.SequenceEqual(arr4);
MessageBox.Show(arrayCompare1 + " " + arrayCompare2);
}
You need to override the Object.Equals somehow like this:
public override bool Equals(object other)
{
if (other is IGene)
return Equals((IGene)other);
return base.Equals(other);
}
You need to override bool Equals(object obj). Simplest addition to your code:
public override bool Equals(object obj)
{
return Equals(obj as IGene);
}
I want to sort my List, where T is Products.
The List may contains elememts with duplicate ReportSeqId. I want to sort it according to ReportSeqId.
But the criteria is that if the ReportSeqId = 0 then it should come last.
INPUT :
new ilistProd<Products>()
{
new Products(0, Report1, SSR),
new Products(2, Report2, SBO),
new Products(0, Report3, PST),
new Products(3, Report4, ABR),
new Products(1, Report5, OSS),
new Products(0, Report6, TCP),
}
OUTPUT:
new ilistProd<Products>()
{
new Products(1, Report5, OSS),
new Products(2, Report2, SBO),
new Products(3, Report4, ABR),
new Products(0, Report3, PST),
new Products(0, Report6, TCP),
new Products(0, Report1, SSR)
}
Below is my code :
public class Products
{
//ctor
public SDVar(int xiReportSeqId, string xiReportName, string xiProduct)
{
this.ReportSeqId = xiReportSeqId;
this.ReportName = xiReportName;
this.Product = xiProduct;
}
public int ReportSeqId {get; set;}
public string ReportName {get; set;}
public string Product {get; set;}
}
public class SDVar
{
//ctor
public SDVar()
{
}
public void DoSort(ref List<Products> ilistProd)
{
ilistProd.Sort(delegate(Products x, Products y)
{
if (x.ReportSeqId == 0)
{
if (y.ReportSeqId == 0)
{
return 0;
}
return -1;
}
return x.ReportSeqId.CompareTo(y.ReportSeqId);
}
}
}
Try this
list.Sort(delegate(Products x, Products y)
{
if(x.ReportSeqId == 0)
return 1;
if(y.ReportSeqId == 0)
return -1;
return x.ReportSeqId.CompareTo(y.ReportSeqId);
});
Normally my preferred solution would be to add an extra property (e.g. SortIndex) which can be used in either Linq, or in a sort delegate (where id 0 would return an int.maxvalue), but to get the existing code to work, you should do an extra check to see of the second id is 0, if the first id is not:
if (x.ReportSeqId == 0)
{
if (y.ReportSeqId == 0)
{
return 0;
}
return 1;
}
else if (y.ReportSeqId == 0)
return -1;
return x.ReportSeqId.CompareTo(y.ReportSeqId);
Another way is to implement IComparable
public class Product : IComparable<Product>
{
private int ReportSeqId = 0;
public int CompareTo(Product other)
{
if (ReportSeqId == 0 || other == null) return 1;
if (other.ReportSeqId == 0) return - 1;
return ReportSeqId - other.ReportSeqId;
}
}
Using LINQ:
products = products.OrderBy(p => p.ReportSeqId == 0 ? Int32.MaxValue : p.ReportSeqId).ToList();
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;
}
}