Linq Distinct does not function as per forum and Microsoft examples - c#

using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace UnitTest.Model
{
[TestFixture]
public class SampleEquatableObjectTest
{
[Test]
public void TwoIdenticalUsersComparedEqualTrue()
{
var user1 = new SampleObject { Id = 1, Name = "Test User" };
var user2 = new SampleObject { Id = 1, Name = "Test User" };
Assert.IsTrue(user1.Equals(user2));
}
[Test]
public void TwoDifferentUsersComparedEqualFalse()
{
var user1 = new SampleObject { Id = 1, Name = "Test User 1" };
var user2 = new SampleObject { Id = 2, Name = "Test User 2" };
Assert.IsFalse(user1.Equals(user2));
}
[Test]
public void CollectionOfUsersReturnsDistinctList()
{
var userList = new List<SampleObject>
{
new SampleObject {Id = 1, Name = "Test User"},
new SampleObject {Id = 1, Name = "Test User 1"},
new SampleObject {Id = 2, Name = "Test User 2"}
};
Assert.AreEqual(userList.Count, 3);
var result = userList.Distinct();
Assert.AreEqual(result.Count(), 2);
var multipleTest = (from r in result group r by new { r.Id } into multGroup where multGroup.Count() > 1 select multGroup.Key).Any();
Assert.IsFalse(multipleTest);
}
public class SampleObject : IEquatable<SampleObject>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(SampleObject other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(other, null) || ReferenceEquals(this, null))
return false;
return Id.Equals(other.Id);
}
}
}
}
The distinct method in this test case does not return a distinct list. The assert for count will fail. I looked at other similar questions and Microsoft examples but they look exactly like the code I have in the test. Any input?

You also need to override the GetHashCode() and Equals methods from the Object class. For more information see this corresponding FXCOP violation.
Then your tests will work like expected.
public class SampleObject : IEquatable<SampleObject>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(SampleObject other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(other, null) || ReferenceEquals(this, null))
return false;
return Id.Equals(other.Id);
}
public override int GetHashCode()
{
return Id;
}
public override bool Equals(object obj)
{
return Equals(obj as SampleObject);
}
}

You're implementing an interface without defining both members to the specification of the interface. Your code needs to look like this:
public bool Equals(SampleObject x, SampleObject y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
return x.Id.Equals(y.Id);
}
public int GetHashCode(SampleObject obj)
{
public int GetHashCode(SampleObject obj)
{
if (Object.ReferenceEquals(obj, null)) return 0;
int hashId = obj.Id == null ? 0 : obj.Id.GetHashCode();
int hashName = obj.Name == null ? 0 : obj.Name.GetHashCode();
return hashId ^ hashName; // or what ever you want you hash to be, hashID would work just as well.
}
}

Related

EqualityComparer on a nested object

I would like to compare two list of nested objects. If the parent objects Id differ and/or any of the childrens Id or Baz property differs, I want to consider them changed.
I've implemented my own version of Equals and GetHashCode below, but despite using my own equalitycomparer, Except() still yields a result, while I expect the objects to be equal.
var foo1 = new Foo
{
Id = 1,
Bars = new List<Bar>
{
new Bar
{
Id = 1,
Baz = 1.5
},
new Bar
{
Id = 1,
Baz = 1.5
}
}
};
var foo2 = new Foo
{
Id = 1,
Bars = new List<Bar>
{
new Bar
{
Id = 1,
Baz = 1.5
},
new Bar
{
Id = 1,
Baz = 1.5
}
}
};
var diff = new[] { foo1 }.Except(new[] { foo2 });
public class Foo
{
private sealed class IdBarsEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo 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.Id == y.Id && Equals(x.Bars, y.Bars);
}
public int GetHashCode(Foo obj)
{
unchecked
{
return (obj.Id * 397) ^ (obj.Bars != null ? obj.Bars.GetHashCode() : 0);
}
}
}
public static IEqualityComparer<Foo> IdBarsComparer { get; } = new IdBarsEqualityComparer();
public int Id { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
protected bool Equals(Bar other)
{
return Id == other.Id && Baz.Equals(other.Baz);
}
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((Bar) obj);
}
public override int GetHashCode()
{
unchecked
{
return (Id * 397) ^ Baz.GetHashCode();
}
}
public int Id { get; set; }
public double Baz { get; set; }
}
There are three things wrong in your code:
You are not passing the equality comparer to Except method, so it's not being used.
Your GetHashCode implementation in Foo is wrong, it returns different results for same objects, so the Equals method is never called.
You are calling equals on two lists: Equals(x.Bars, y.Bars), this checks for reference equality. You can use SequenceEqual instead to compare elements one by one: x.Bars.SequenceEqual(y.Bars)

Compare Items in two Lists and return non matching Items

So I have two Lists.
List<Farmer> CSVFarmer;
List<Farmer> Farmers;
CSVFarmer List gets its items from a method that will read a csv file.
Farmers List gets its items from a table in a sql database;
Now what I want to do is compare the two lists and return a list of non matching items;
For example if List CSVFarmer has:
FarmerName ContractNumber ContactNumber
John 2468 12345
Mike 13579 15790
And List Farmers has:
FarmerName ContractNumber ContactNumber
Mike 13579 15790
The list being returned should only have one item in it : Farmer John.
Farmer Class:
public class Farmer:INotifyPropertyChanged
{
int _id;
string _firstName;
string _farmerNo;
string _contactNumber;
public Farmer()
{
_firstName = string.Empty;
_farmerNo = string.Empty;
_contactNumber = string.Empty;
}
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string FarmerNo
{
get { return _farmerNo; }
set
{
_farmerNo = value;
OnPropertyChanged("FarmerNo");
}
}
public string ContactNumber
{
get { return _contactNumber; }
set
{
_contactNumber = value;
OnPropertyChanged("ContactNumber");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged (string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
Ive tried this:
public class vmUserManagement
{
public List<Farmer> Farmer { get; set; }
public List<Farmer> CSVFarmers { get; set; }
public List<Farmer> Farmers { get; set; }
public vmUserManagement()
{
CSVFarmers = new List<Farmer>();
Farmers = new List<Farmer>();
Farmer = new List<Farmer>();
}
public List<Farmer> getAllFarmers()
{
Farmers = RepoDapper.getAllFarmers();
return Farmers;
}
public List<Farmer> CSVImportFarmer()
{
OpenFileDialog openFile = new OpenFileDialog();
openFile.DefaultExt = ".csv";
openFile.Filter = "(.csv) | *.csv";
var browseFile = openFile.ShowDialog();
if (browseFile == true)
{
string FilePath = openFile.FileName;
List<Farmer> values = File.ReadAllLines(FilePath).Select(v => FromFarmerCsv(v)).ToList();
CSVFarmers = values;
}
return CSVFarmers;
}
public static Farmer FromFarmerCsv(string csvLine)
{
string[] values = csvLine.Split(',');
Farmer farmer = new Farmer();
farmer.FirstName = values[0];
farmer.FarmerNo = values[1];
farmer.ContactNumber = values[2];
return farmer;
}
public List<Farmer> validateFarmerList()
{
foreach (var a in CSVFarmers)
{
foreach (var b in Farmers)
{
if (a != b)
{
Farmer.Add(a);
}
}
}
return Farmer;
}
}
The problem I'm having is that I will end up with two entries in List Farmer. Both for Farmer John and Farmer Mike. When I should only be getting a List containing Farmer John. Why is that?
I've also tried using Except:
public List<Farmer> validateFarmerList()
{
Farmer = CSVFarmers.Except(Farmers).ToList();
return Farmer;
}
But I still get two items in my Farmer List (Farmer John and Mike) instead of one.
Am I missing something? Any help will be much appreciated.
You have't overridden Equals and GethashCode in your Farmer class. That's why (a != b) doesn't work and also Enumerable.Except fails for the same reason: only references are compared and both are different instances.
How should .NET know that the ContractNumber of the farmer is relevant to identify him? One wayy is to tell it by overriding Equals and GetHashCode:
public class Farmer : IEquatable<Farmer>
{
public string FarmerName { get; set; }
public string ContractNumber { get; set; }
// .... other properties etc
public bool Equals(Farmer other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(ContractNumber, other.ContractNumber);
}
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((Farmer) obj);
}
public override int GetHashCode()
{
return (ContractNumber != null ? ContractNumber.GetHashCode() : 0);
}
}
Now you can use Except:
Farmer = CSVFarmers.Except(Farmers).ToList();
2nd way is to implement a custom IEqualityComparer<Farmer>, f.e if you can't change the Farmer class itself or you don't want to change it's behaviour and just want a custom comparer:
public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}public class FarmerContractComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(null, x) || ReferenceEquals(null, y)) return false;
return x.ContractNumber == y.ContractNumber;
}
public int GetHashCode(Farmer obj)
{
return (obj.ContractNumber != null ? obj.ContractNumber.GetHashCode() : 0);
}
}
You can use this comparer in many LINQ methods, for example also in Enumerable.Except:
Farmer = CSVFarmers.Except(Farmers, new FarmerContractComparer()).ToList();
This approach has the advantage that you could provide different comparers for different tasks.
3rd approach: use LINQ and don't create a new class(less reusable and efficient but less work):
Farmer = CSVFarmers.Where(f => !Farmers.Any(f2 => f.ContractNumber == f2.ContractNumber)).ToList();
You will need to either create a class implementing IEqualityComparer<Farmer> and pass an instance of that as a second parameter to .Except() or implement IEquatable<Farmer> on your Farmer class.
The other answer already has a good implementation of the latter. If you always want farmers to be equal when their contract number is equal, use that one.
So if you want your Except work on contract numbers, but in other places you want other criteria, you need the first option:
class FarmerEqualWhenContractEqualComparer : IEqualityComparer<Farmer>
{
public bool Equals(Farmer x, Farmer y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the farmers' contracts are equal.
return x.ContractNumber == y.ContractNumber;
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(Farmer farmer)
{
//Check whether the object is null
if (Object.ReferenceEquals(product, null)) return 0;
//Get hash code for the Name field if it is not null.
return farmer.ContractNumber?.GetHashCode() ?? 0;
}
}
Then you can do:
var changedOrNew = CSVFarmers.Except(Farmers, new FarmerEqualWhenContractEqualComparer()).ToList();

Creating a IEqualityComparer<IEnumerable<T>>

I'm using xUnit and it doesn't have a way to determine if 2 IEnumerable<T> are equal if T is custom type.
I've tried using LINQ SequenceEqual but again as the instances of T are different this returns false;
Here is a basic test with a non-working IEqualityComparer
[Fact]
public void FactMethodName()
{
var one = new[] { new KeywordSchedule() { Id = 1 } };
var two = new[] { new KeywordSchedule() { Id = 1 } };
Assert.Equal(one, two, new KeywordScheduleComparer());
}
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
I'm using this in an integration test, so I insert data from a IEnumerable into a DB at the start, then call my SUT to retrieve data from DB and compare.
If you can help me get a collection comparison working I'd appreciate it!
I just verified that this works fine with xUnit.net 1.9.2:
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
}
public class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass x, MyClass y)
{
return x.ID == y.ID;
}
public int GetHashCode(MyClass obj)
{
return obj.ID.GetHashCode();
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Brad" };
var obj2 = new MyClass { ID = 42, Name = "Joe" };
Assert.Equal(new[] { obj1 }, new[] { obj2 }, new MyClassComparer());
}
}
So I'm not 100% clear why you need the extra comparer. Just the single comparer should be sufficient.
Well, your implementation is pending. You implemented custom comparer for IEnumerable<KeywordSchedule> but forgot to implement the same for KeywordSchedule.
x.SequenceEqual Still uses Comparer<T>.Default so it goes for reference comaprison and hence result is false.
public class KScheduleComparer : IEqualityComparer<KeywordSchedule>
{
public bool Equals(KeywordSchedule x, KeywordSchedule y)
{
return x.Id == y.Id;
}
public int GetHashCode(KeywordSchedule obj)
{
return obj.GetHashCode();
}
}
Then modify your Equals method in KeywordScheduleComparer class as below
public class KeywordScheduleComparer : IEqualityComparer<IEnumerable<KeywordSchedule>>
{
public bool Equals(IEnumerable<KeywordSchedule> x, IEnumerable<KeywordSchedule> y)
{
return Object.ReferenceEquals(x, y) || (x != null && y != null && x.SequenceEqual(y, new KScheduleComparer()));
}
public int GetHashCode(IEnumerable<KeywordSchedule> obj)
{
if (obj == null)
return 0;
return unchecked(obj.Select(e => e.GetHashCode()).Aggregate(0, (a, b) => a + b)); // BAD
}
}
You could do this more elegantly using FluentAssertions library. It has plenty assertion methods for collections.
public class MyClass
{
public int ID { get; set; }
public string Name { get; set; }
protected bool Equals(MyClass other)
{
return ID == other.ID;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((MyClass) obj);
}
public override int GetHashCode()
{
unchecked
{
return (ID*397) ^ (Name != null ? Name.GetHashCode() : 0);
}
}
}
public class ExampleTest
{
[Fact]
public void TestForEquality()
{
var obj1 = new MyClass { ID = 42, Name = "Rock" };
var obj2 = new MyClass { ID = 42, Name = "Paper" };
var obj3 = new MyClass { ID = 42, Name = "Scissors" };
var obj4 = new MyClass { ID = 42, Name = "Lizard" };
var list1 = new List<MyClass> {obj1, obj2};
list1.Should().BeEquivalentTo(obj3, obj4);
}
}

How to check for common objects between 2 generic lists

I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);

C# List merging without repeating

My question is at the end of the following code in comments:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class MyObj
{
int m_id;
public MyObj(int id)
{
this.m_id = id;
}
public int GetId()
{
return this.m_id;
}
}
class Program
{
static void Main(string[] args)
{
var m0 = new MyObj(0);
var m1 = new MyObj(1);
var m2 = new MyObj(2);
var m3 = new MyObj(3);
var m4 = new MyObj(4);
List<MyObj> refList1 = new List<MyObj>()
{
m0, m1, m2, m3
};
List<MyObj> refList2 = new List<MyObj>()
{
m1, m2, m3, m4
};
//How to merge refList2 into refList1 without id repeating,
//so refList1 must be [m0, m1, m2, m3, m4]
}
}
}
You would do this using LINQ, with:
var resultList = refList1.Union(refList2).ToList();
However, this requires either that MyObj implements IEquatable<MyObj>, or the other overload of Union is used which takes an IEqualityComparer<T> parameter.
The first solution would need this change:
class MyObj : IEquatable<MyObj>
{
int m_id;
public MyObj(int id)
{
this.m_id = id;
}
public int GetId()
{
return this.m_id;
}
public bool Equals(MyObj other)
{
if (ReferenceEquals(null, other)) {
return false;
}
if (ReferenceEquals(this, other)) {
return true;
}
return other.m_id == this.m_id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) {
return false;
}
if (ReferenceEquals(this, obj)) {
return true;
}
if (obj.GetType() != typeof(MyObj)) {
return false;
}
return Equals((MyObj)obj);
}
public override int GetHashCode()
{
return this.m_id;
}
}
The second solution would go like:
class MyComparer : IEqualityComparer<MyObj>
{
public bool Equals(MyObj x, MyObj y)
{
return x.GetId() == y.GetId();
}
public int GetHashCode(MyObj obj)
{
return obj.GetId();
}
}
var resultList = refList1.Union(refList2, new MyComparer()).ToList();
and would not require changes to class MyObj.
You can use Union
Have a look here.
Use the Union Extension method.
var merged = refList2.Union(refList1).ToList();
And define the Equals/Hashcode methods. Once you do this it will automatically remove the duplicates.
class MyObj{
/*your stuff*/
public override bool Equals(object obj)
{
var r_list= obj as MyObj;
if(r_list == null) return false;
return this._m_id==r_list.m_id;
}
public override int GetHashCode()
{
return m_id;
}
}
refList1 = refList1.Union(refList2).Distinct.ToList();

Categories

Resources