I have two lists:
List<User> collection1 = new List<User>();
List<User> collection2 = new List<User>();
1) I have to get all items common to both of the lists using LINQ. However, the class User has a lot of properties and I just want to compare FirstName and LastName.
2) How can I get the items in collection1 but not in collection2 using the same comparison rule?
Use Enumerable.Intersect for the first question and Enumerable.Except for the second. To wit:
var common = collection1.Intersect(collection2, new UserEqualityComparer());
var difference = collection1.Except(collection2, new UserEqualityComparer());
Here, of course, I am assuming that UserEqualityComparer implements IEqualityComparer<User> like so:
class UserEqualityComparer : IEqualityComparer<User> {
public bool Equals(User x, User y) {
if (Object.ReferenceEquals(x, y)) {
return true;
}
if (x == null || y == null) {
return false;
}
return x.FirstName == y.FirstName && x.LastName == y.LastName;
}
public int GetHashCode(User obj) {
if (obj == null) {
return 0;
}
return 23 * obj.FirstName.GetHashCode() + obj.LastName.GetHashCode();
}
}
Related
I have two list that needs to be compared and should exclude duplicates by their external Id, however the result that I always get is the list of records that have the same external Id in both list.
Code:
public class FooComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.ExternalId == y.ExternalId;
}
public int GetHashCode(Foo foo)
{
if (ReferenceEquals(foo, null)) return 0;
return foo.ExternalId.GetHashCode();
}
}
var bulkFoos = _bulkFoo.GetBulkFoos();
var fooFromXmlList = Mapper.Map<List<Foo>>(bulkFoos);
var foosWithUniqueId = _fooRepository.GetAllWithExternalId();
var fooWithUniqueIdList = Mapper.Map<List<Foo>>(foosWithUniqueId);
var fooList = fooFromXmlList.Except(fooWithExternalIdList, new FooComparer());
Your GetHashCode implementation should use the same properties as those for equality, however yours uses foo.Id for the hash code and foo.externalId for equality. Change GetHashCode to use externalId:
public int GetHashCode(Foo foo)
{
if(ReferenceEquals(foo, null)) return 0;
else return foo.externalId.GetHashCode();
}
I was wondering what was the best approach to compare multiple objects that are created and having the state of the objects changed to Inactive (Deleted), while creating history and dependencies.
This also means im comparing past and present objects inside a relational table (MarketCookies).
Id | CookieID | MarketID
The ugly solution i found was calculating how many objects had i changed.
For this purpose lets call the items of the Past: ListP
And the new items: ListF
I divided this method into three steps:
1 - Count both lists;
2 - Find the objects of ListP that are not present in List F and change their state to Inactive and update them;
3 - Create the new Objects and save them.
But this code is very difficult to maintain.. How can i make an easy code to maintain and keep the functionality?
Market Modal:
public class Market()
{
public ICollection<Cookie> Cookies {get; set;}
}
Cookie Modal:
public class Cookie()
{
public int Id {get;set;}
//Foreign Key
public int CookieID {get;set}
//Foreign Key
public int MarketID {get;set;}
}
Code:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
int ListPCount = ListP.Count();
int ListFCount = ListF.Count();
if(ListPCount > ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete the Object
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Object
}
});
}
else if(ListPCount < ListFCount)
{
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
}
else if(ListPCount == ListFCount)
{
ListP.Foreach(x =>
{
var ItemExists = ListF.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Delete Objects
}
});
ListF.Foreach(x =>
{
var ItemExists = ListP.Where(y => y.Id == x.Id).FirstOrDefault();
if(ItemExists == null)
{
//Create Objects
}
});
}
}
Without a good, minimal, complete code example that clearly illustrates the question, it's hard to know for sure what even a good implementation would look like, never mind "the best". But, based on your description, it seems like the LINQ Except() method would actually serve your needs reasonably well. For example:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
foreach (var item in ListP.Except(ListF))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP))
{
// create new object
}
}
This of course assumes that your objects have overridden Equals() and GetHashCode(). If not, you can provide your own implementation of IEqualityComparer<T> for the above. For example:
// General-purpose equality comparer implementation for convenience.
// Rather than declaring a new class for each time you want an
// IEqualityComparer<T>, just pass this class appropriate delegates
// to define the actual implementation desired.
class GeneralEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _getHashCode;
public GeneralEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
public bool Equals(T t1, T t2)
{
return _equals(t1, t2);
}
public int GetHashCode(T t)
{
return _getHashCode(t);
}
}
Used like this:
public void UpdateMarket (Market Market, int Id)
{
var ListP = MarketCookiesRepository.GetAll()
.Where(x => x.MarketID == Id && Market.State != "Inactive").ToList();
var ListF = Market.Cookies.ToList();
IEqualityComparer<Cookie> comparer = new GeneralEqualityComparer<Cookie>(
(t1, t2) => t1.Id == t2.Id, t => t.Id.GetHashCode());
foreach (var item in ListP.Except(ListF, comparer))
{
// set to inactive
}
foreach (var item in ListF.Except(ListP, comparer))
{
// create new object
}
}
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.
I tried three ways of comparing two lists but not a single way is working.
static void Main(string[] args)
{
var list1 = new List<A>()
{
{new A(){Id = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA")}},
{new A(){Id = Guid.Parse("90DF3989-16FC-4E2B-A0C7-A3640156D6F2")}}
};
var list2 = new List<A>()
{
{new A(){Id = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA")}},
{new A(){Id = Guid.Parse("90DF3989-16FC-4E2B-A0C7-A3640156D6F2")}}
};
var test1 = ListEuality<A>(list1, list2, );
var areEquivalent = (list1.Count == list2.Count) && !list1.Except(list2).Any();
var test = list1.OrderBy(t => t.Id).SequenceEqual(list2.OrderBy(t => t.Id));
Console.ReadLine();
}
//This Method compares two lists for equality without taking consideration of order.
public static bool ListEuality<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
var cnt = new Dictionary<T, int>(comparer);
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else
{
return false;
}
}
//If dictionary element becomes 0 means dictionary modified for each item that means items are present in both list
return cnt.Values.All(c => c == 0);
}
class A
{
public Guid Id;
}
Probably all you need to do is to override the Equals method for your A object like so:
public override bool Equals(Object obj) {
if (obj == null) return false;
A objA = obj as A;
if (objA == null) return false;
return (this.Id.Equals(objA.Id));
}
This code:
void Main()
{
Guid g = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA");
A a = new A();
a.Id = g;
Guid h = Guid.Parse("1BA3B3A3-FD4C-E311-B616-A41F729385FA");
A b = new A();
b.Id = h;
bool eq = a.Equals(b);
Console.WriteLine(eq);
}
// Define other methods and classes here
public class A {
public Guid Id;
public override bool Equals(Object obj) {
if (obj == null) return false;
A objA = obj as A;
if (objA == null) return false;
return (this.Id.Equals(objA.Id));
}
}
If you override Equals, it returns True. If you remove the Equals method for the A-class, it returns False.
The reason is that you are using different instances of A. A way to do it without overriding comparison for A would be
bool areEquivalent = (list1.Count == list2.Count) && !list1.Select(a => a.ID).Except(list2.Select(a => a.ID).Any();
Meaning you only compare the GUIDs
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.