I have a custom list class let say,
public class Fruit
{
public string Name { get; set; }
public string Size { get; set; }
public string Weight{ get; set; }
}
Now I am adding records to it like this,
List<Fruit> Fruits= new List<Fruit>();
//some foreach loop
Fruit fruit = new Fruit();
fruit.Name = ...;
fruit.Size = ...;
fruit.Weight = ...;
Fruits.Add(fruit);
What I want ?
I want to make changes to Public Fruit Class in a way that it checks if any of fruit in custom list has already has same weight then just ignore it and continue e.g. don't add it to the list.
I would prefer doing it without changing foreach loop logic
Use LINQ .Any() - Determines whether any element of a sequence exists or satisfies a condition. (MSDN: http://msdn.microsoft.com/en-us/library/system.linq.enumerable.any.aspx)
if (!Fruits.Any(f => fruit.Weight != null && f.Weight == fruit.Weight))
Fruits.Add(fruit);
If duplicate weights are not allowed i would use a HashSet<Fruit> with a custom IEqualityComparer:
public class FruitWeightComparer : IEqualityComparer<Fruit>
{
public bool Equals(Fruit x, Fruit y)
{
if(x == null || y== null) return false;
if (Object.ReferenceEquals(x, y)) return true;
return x.Weight == y.Weight;
}
public int GetHashCode(Fruit obj)
{
return obj.Weight == null ? 0 : obj.Weight.GetHashCode();
}
}
Now you can use the HashSet constructor with this comparer:
HashSet<Fruit> Fruits = new HashSet<Fruit>(new FruitWeightComparer());
// ...
bool notInSet = Fruits.Add(fruit);
HashSet.Add returns true if the item could be added.
You can control it at insert time by simply not inserting already existing fruit
if (!myFruits.Any(f => f.Weight == newFruit.Weight))
myFruits.Add(newFruit);
If you can't manipulate the insertion logic you can make a custom list that wraps a normal List<T> and changes the behavior of Add like in the above example:
public class FruitsWithDistinctWeightList : IEnumerable<Fruit>
{
private List<Fruit> internalList;
... // Constructor etc.
public void Add(Fruit fruit)
{
if (!internalList.Any(f => f.Weight == fruit.Weight))
internalList.Add(fruit);
}
... // Further impl of IEnumerable<Fruit> or IList<Fruit>
}
You could also use some existing collection that does not allow duplicate items. For example some hash based collection such as HashSet<Fruit>:
var fruitsWithDistinctWeight = new HashSet<Fruit>(new FruitWeightComparer());
Where you'd use a comparer that says fruits with equal weight are equal:
public class FruitWeightComparer : IEqualityComparer<Fruit>
{
public bool Equals(Fruit one, Fruit two)
{
return one.Weight == two.Weight;
}
public int GetHashCode(Fruit item)
{
return one.Weight.GetHashCode();
}
}
Note that a HashSet<T> is not ordered like a list is. Note that all of the code above for simplicity assumes that the Weight field is set. If you have public setters on your class (i.e. no guarantees of this) you would have to change appropriately.
Related
I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
I am searching for the C# equivalent of assertJ
Assert.That(object1).isEqualToComparingFieldByField(object2)
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
C# classes are reference equality, which means that variables are the same using the standard Equals and == if they point to the same object, you could override that behaivour, but it may break something now or in the future.
Or, you could switch to using a construct that's value equality by default, which structs as well as record classes are. If you can't (or don't want to) do that you can implement a value equals "helper" method yourself. I would not recommend overriding the Equals method or the == operator, as that can (and most likely will) lead to errors in the future instead I recommend you write your own ValueEquals method or extension method, something along the lines of
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
Depending on whether or not you can/ want to add a ValueEquals to your Foo class you can decide on doing it with an extension method or a normal method.
You could also implement a IEqualityComparer<T> like
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
Or, you could write a generic ValueEquals that works for all^* classes using Reflection:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
This implementation is by no means perfect, and should honestly only be used for UnitTests and also should be thoroughly tested itself. You can see my tests as a dotnetfiddle here
Or you could do it "dirty" and serialize the objects to a string and compare those values.
This is how the custom object is defined:
public class AccountDomain
{
public string MAILDOMAIN { get; set; }
public string ORG_NAME { get; set; }
}
This is how I am populating the List of objects:
List<AccountDomain> mainDBAccountDomain = mainDB.GetAllAccountsAndDomains();
List<AccountDomain> manageEngineAccountDomain = ManageEngine.GetAllAccountsAndDomains();
This code works fine - if I look at the locals windows I can see a List of Objects in both mainDBAccountDomain and manageEngineAccountDomain.
I'm struggling with the next bit, ideally I want a new list of type AccountDomain that contains all entries that are in mainDBAccountDomain and not ManageEngineAccountDomain
Any help greatly appreciated, even if it's just a pointer in the right direction!
I want a new list of type AccountDomain that contains all entries that are in mainDBAccountDomain and not ManageEngineAccountDomain
It's very simple with linq to objects, it's exactly what the Enumerable.Except function does:
var result = mainDBAccountDomain.Except(manageEngineAccountDomain).ToList();
You can pass a comparer to the Except function if you need something different from reference equality, or you could implement Equals and GetHashCode in AccountDomain (and optionally implement IEquatable<AccountDomain> on top of these).
See this explanation if you need more details about comparers.
Here's an example:
public class AccountDomainEqualityComparer : IEqualityComparer<AccountDomain>
{
public static readonly AccountDomainEqualityComparer Instance
= new AccountDomainEqualityComparer();
private AccountDomainEqualityComparer()
{
}
public bool Equals(AccountDomain x, AccountDomain y)
{
if (ReferenceEquals(x, y))
return true;
if (x == null || y == null)
return false;
return x.MAILDOMAIN == y.MAILDOMAIN
&& x.ORG_NAME == y.ORG_NAME;
}
public int GetHashCode(AccountDomain obj)
{
if (obj == null)
return 0;
return (obj.MAILDOMAIN ?? string.Empty).GetHashCode()
^ (397 * (obj.ORG_NAME ?? string.Empty).GetHashCode());
}
}
Then, you use it like this:
var result = mainDBAccountDomain.Except(manageEngineAccountDomain,
AccountDomainEqualityComparer.Instance)
.ToList();
I have several classes in my c# application.
I have a plant class with a constructor that takes a name and a weight.
Then I have a Fruit class that inherits plant and adds the number of seeds attribute.
Also I have a Veg class that inherits from plant and adds the savoryLevel attribute.
fruit and veg can be added to their lists by the user.
I have overloaded the == operator in fruit so that it compares the names of fruit and veg and if they have the same name it tells you. My issue is when I try to compare the whole lists to find duplication, I just cant get the code to work at all.
here is some of my code
plant class
public string name;
public string weight;
public Plant(string name, string weight)
{
this.name = name;
this.email = weight;
}
....
public static bool operator ==(Plant a, Plant b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.name == b.name;
}
then the new fruit constructor
string seeds;
public fruit(string name, string weight, string seeds)
: base(name, weight)
{
this.seeds
}
here is veg
string savoryLevel;
public fruit(string name, string weight, string savouryLevel)
: base(name, weight)
{
this.savoryLevel
}
here is the main where I compare 2 instances, this works fine
Fruit f = new Fruit("apple", "2", "5");
Veg v = new Veg("carrot", "3", "7");
if (f == v)
{
Console.WriteLine("They are the same");
}
else{
Console.WriteLine("They are different");
}
This is the tricky part, I need to iterate through my entire list of veg and fruit and see if any of the fruit have the same name as the veg.
using the lists directly wont work
List<Fruit> fr = new List<Fruit>();
List<Veg> ve = new List<Veg>();
if(fr == ve){
Console.....
}
else{
Console....
}
So how do I get the lists to compare and print out some result to say these are the same or these are not the same?
Any help is really appreciated, thanks.
please just ask if you would like more info.
I think you should use IEquatable<Plant> and cast the lists into List<Plant> with SequenceEquals()
Demo:
public class Plant : IEquatable<Plant>
{
public string Name { get; set; }
public string Weight { get; set; }
public override bool Equals(object obj)
{
var other=obj as Plant;
if(other!=null)
{
return Equals(other);
}
return false;
}
public bool Equals(Plant other)
{
Debug.WriteLine("Checking Equality Between {0} And {1}", Name, other.Name);
return Name.Equals(other.Name);
}
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
public class Fruit : Plant
{
public string Seeds { get; set; }
}
public class Veg : Plant
{
public string SavoryLevel { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Fruit> fruits=new List<Fruit>() {
new Fruit() { Name="apple", Weight = "2", Seeds="5" },
new Fruit() { Name="banana", Weight="1", Seeds="30" }
};
List<Veg> veggies=new List<Veg>() {
new Veg() { Name = "carrot", Weight="3", SavoryLevel="7" },
new Veg() { Name = "potato", Weight="5", SavoryLevel="1" }
};
var equal=fruits.Cast<Plant>().SequenceEqual(veggies);
var unique_fruits=fruits.Distinct();
}
}
It produces the output
Checking Equality Between apple And carrot
and then the equality comparison ends (since it is false). The point being is that it calls the appropriate Equals() function.
If you wanna compare the items at the same index Zip method can be useful:
bool result = fr.Zip(ve, (f,v) => new { f, v }).All(x => x.f == x.v);
Zip methods create pairs of corresponding items, then put each pair into an anonymous type. And All method simply checks if all items in the pairs are equal.
if you want to to it per item, you could do it like this
foreach(var fruit in fr)
{
if(ve.Any(x => x.Name == fruit.Name))
{
Console.Write(fruit.Name + " is in both lists");
}
}
I'd use LINQ, and rather that (or in addition to) overloading the == operator, go for the "more native" object.Equals and object.GetHashCode.
public override int GetHashCode()
{
return this.name.GetHashCode();
}
public override bool Equals(object b)
{
Plant bPlant = b as Plant;
// If one is null, but not both, return false.
if (bPlant == null)
{
return false;
}
// Return true if the fields match:
return this.name == b.name;
}
Then you can use LINQ:
return fr.SequenceEquals(ve);
Note, of course, that, as the name implies, this only works when fr and ve are exactly equal. That is to say, the order must be the same between them: if both contain "Carrot, Broccoli," you'll be fine, but if one is that and the other is "Broccoli, Carrot," this will return false.
Unless I'm misunderstanding, and in fact you want the intersection, not to know that they're equal, in which case:
return fr.Intersect(ve);
You really don't need to overload the Equals method to find out if something is different. Overloading the Equals method should be used if you are looking for a different behaviour, not for different results.
Since this case compares two string members in two different classes, why not just use LINQ to compare the members themselves which are of the same datatype?
using System;
using System.Collections.Generic;
using System.Linq;
public class Test
{
public static void Main()
{
List<Fruit> f = new List<Fruit>();
Fruit fTemp = new Fruit() { name = "Kiwi" };
f.Add(fTemp);
fTemp = new Fruit() { name = "Tomato" };
f.Add(fTemp);
List<Veg> v = new List<Veg>();
Veg vTemp = new Veg() { name = "Tomato" };
v.Add(vTemp);
List<Veg> vDuplicates = v.Where(vegToCompare=>f.Any(fruitToCompare=>fruitToCompare.name.Equals(vegToCompare.name))).ToList();
vDuplicates.ForEach(a=>Console.WriteLine(a.name));
Console.WriteLine("Number of Duplicates Found: " + vDuplicates.Count);
}
}
public class Fruit
{
public string name;
}
public class Veg
{
public string name;
}
First off thanks everyone for the input, You've all helped me see the problem more clearly.
But Overloading the == operator is something I had to do as part of the requirements.
I have however found a relatively simple way to compare the 2 lists that uses the overloaded == operator I added to the Plant Class
By nesting a forEach loop I check every list item of veg against every list item of fruit.
public void Compare(List<Fruit> frList, List<Veg> vList)
{
foreach (Fruit f in frList)
{
foreach (Veg v in vList)
{
if (f == v)
{
//some functionality
}else{
//some other funtionality
}
}
}
}
This still uses the overloaded == operator in plant and will only compare the name when I call the the method in the main. i.e. even if the plants have different weights they will be considered the same.
Thanks again for the input guys.
I have a custom class called CustomClass. It contains a variable called "Name" and a list of values (for the sake of simplicity let's make this an int - in reality it is another custom class, but the principle should be the same).
So :
public class CustomClass {
string name;
}
I have a List<CustomClass>.
When I attempt to add a value to this List, the logic I want, is for this List to check if it contains a CustomClass with the same name as the value I want to add.
If it does, then do x, otherwise, do y.
listOfCustomClass.Contains(customClassToAdd.name) will not work in this case, I assume, however this is the functionality I require.
What is best practice here ?
I think you can try something like var x = MyList.Where(C=> C.Name == InsertedName) and check the result (not tested)
You'll have to create a new class,let's call it CustomList, that inherits from IList<> where you can override the add method, do your check, and then add it to the base. Something like this:
public class CustomList<T> : IList<T> where T : CustomClass
{
private List<T> innerlist;
public void Add(T item)
{
if(innerlist.Any(a => a.Name == item.Name)))
innerlist.Add(item);
}
}
you can do it using linq as follow but you have to make name field public.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Any(p => p.name.Equals(toCheck)))
{
//do x here
}
else
{
//do y here
}
however if you don't want to use linq then Do some changes in CustomClass as follow
public class CustomClass
{
string name;
List<int> intLost = new List<int>();
public override bool Equals(object obj)
{
return this.Equals(obj as CustomClass);
}
public override int GetHashCode()
{
return 0;
}
public bool Equals(CustomClass cc)
{
if (cc == null) return false;
return this.name.Equals(cc.name);
}
}
Then you can do this.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Contains(toCheck))
{
//do x here
}
else
{
//do y here
}
It seems to me that you want to override the .Add() behavior of your List<CustomClass>. While you could use extension methods, I think a better solution would be to invent a class that extends List in some manner. I'd recommend implementing IList in your collection class if you need to have that level of control over add operations...
public class CustomClassList : IList<CustomClass>
{
public void Add (CustomClass item)
{
if(this.Select(t => t.Name).Contains(item.Name))
// Do whatever here...
else
// Do whatever else here...
}
// ... other IList implementations here ...
}
try this:
IList<CustomClass> list = new List<CustomClass>();
CustomClass customClass = new CustomClass();
customClass.name = "Lucas";
if((list.Tolist().Find(x => x.name == customClass.name)) == null)
{
list.Add(customClass);
}
else
{
//do y;
}
You could override the Equals(object o) function in your CustomClass, so that two CustomClasses are considered equal if their names are the same. Then
listOfCustomClass.Contains(customClassToAdd);
should work.
Another way is to override Equals method on your CustomClass and then just call List.Contains()
If the name property uniquely identifies the CustomClass, then you should overload Equals and GetHashCode(). The reason List.Contains doesn't work is that underneath the HashCodes are compared. So you need to overload GetHashCode and Equals something like this:
public override int GetHashCode()
{
return this.name.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as CustomClass;
if (other != null)
{
if (other.Name == this.Name)
{
return true;
}
}
return false;
}
I'm having troubles with the Except() method.
Instead of returning the difference, it returns the original set.
I've tried implementing the IEquatable and IEqualityComparer in the Account class.
I've also tried creating a separate IEqualityComparer class for Account.
When the Except() method is called from main, it doesn't seem to call my custom Equals() method, but when I tried Count(), it did call the custom GetHashCode() method!
I'm sure I made a trivial mistake somewhere and I hope a fresh pair of eyes can help me.
main:
IEnumerable<Account> everyPartnerID =
from partner in dataContext.Partners
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> hasAccountPartnerID =
from partner in dataContext.Partners
from account in dataContext.Accounts
where
!partner.ID.Equals(Guid.Empty) &&
account.IDPartner.Equals(partner.ID) &&
account.Username.Equals("Special")
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> noAccountPartnerID =
everyPartnerID.Except(
hasAccountPartnerID,
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)));
Account:
public class Account : IEquatable<Account>
{
public Guid IDPartner{ get; set; }
public string Name{ get; set; }
/* #region IEquatable<Account> Members
public bool Equals(Account other)
{
return this.IDPartner.Equals(other.IDPartner);
}
#endregion*/
}
LambdaComparer:
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _lambdaComparer;
private readonly Func<T, int> _lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => o.GetHashCode())
{
}
public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
{
if (lambdaComparer == null)
throw new ArgumentNullException("lambdaComparer");
if (lambdaHash == null)
throw new ArgumentNullException("lambdaHash");
_lambdaComparer = lambdaComparer;
_lambdaHash = lambdaHash;
}
public bool Equals(T x, T y)
{
return _lambdaComparer(x, y);
}
public int GetHashCode(T obj)
{
return _lambdaHash(obj);
}
}
Basically your LambdaComparer class is broken when you pass in just a single function, because it uses the "identity hash code" provider if you don't provide anything else. The hash code is used by Except, and that's what's causing the problem.
Three options here:
Implement your own ExceptBy method and then preferably contribute it to MoreLINQ which contains that sort of thing.
Use a different implementation of IEqualityComparer<T>. I have a ProjectionEqualityComparer class you can use in MiscUtil - or you can use the code as posted in another question.
Pass a lambda expression into your LambdaComparer code to use for the hash:
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)),
x => x.IDPartner.GetHashCode());
You could also quickly fix your LambdaComparer to work when only the equality parameters are supplied like this:
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => 1)
{
}
Look here, how to use and implementing IEqualityComparer in way with linq.Except and beyond.
https://www.dreamincode.net/forums/topic/352582-linq-by-example-3-methods-using-iequalitycomparer/
public class Department {
public string Code { get; set; }
public string Name { get; set; }
}
public class DepartmentComparer : IEqualityComparer {
// equal if their Codes are equal
public bool Equals(Department x, Department y) {
// reference the same objects?
if (Object.ReferenceEquals(x, y)) return true;
// is either null?
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Code == y.Code;
}
public int GetHashCode(Department dept) {
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(dept, null)) return 0;
return dept.Code.GetHashCode();
}
}
IEnumerable<Department> deptExcept = departments.Except(departments2,
new DepartmentComparer());
foreach (Department dept in deptExcept) {
Console.WriteLine("{0} {1}", dept.Code, dept.Name);
}
// departments not in departments2: AC, Accounts.
IMO, this answer above is the simplest solution compared to other solutions for this problem. I tweaked it such that I use the same logic for the Object class's Equals() and GetHasCode(). The benefit is that this solution is completely transparent to the client linq expression.
public class Ericsson4GCell
{
public string CellName { get; set; }
public string OtherDependantProperty { get; set; }
public override bool Equals(Object y)
{
var rhsCell = y as Ericsson4GCell;
// reference the same objects?
if (Object.ReferenceEquals(this, rhsCell)) return true;
// is either null?
if (Object.ReferenceEquals(this, null) || Object.ReferenceEquals(rhsCell, null))
return false;
return this.CellName == rhsCell.CellName;
}
public override int GetHashCode()
{
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(this, null)) return 0;
return this.CellName.GetHashCode();
}
}