How to handle nulls in IEqualityComparer? - c#

The following method is from XUnit Assert class:
public static void Equal<T>(IEnumerable<T> expected, IEnumerable<T> actual, IEqualityComparer<T> comparer);
And I am using it as:
IEnumerable<Decimal?> x = getXValues();
IEnumerable<Decimal?> y = getYValues();
Assert.Equal(x, y, new DecimalToleranceEqualityComparer(0.01m));
I am using an IEqualityComparer because is fine to consider 2.526 equal to 2.524.
I get an error because DecimalToleranceEqualityComparer is only for Decimal ...
x and y might have null values. DecimalToleranceEqualityComparer is:
public class DecimalToleranceEqualityComparer : IEqualityComparer<Decimal> {
private readonly Decimal _tolerance;
public DecimalToleranceEqualityComparer(Decimal tolerance) {
_tolerance = tolerance;
}
public Boolean Equals(Decimal x, Decimal y) {
return Math.Abs(x - y) <= _tolerance;
}
public Int32 GetHashCode(Decimal obj) {
return obj.GetHashCode();
}
}
I suppose if 2 values are nulls they should be consider equal ...
How to change the IEqualityComparer so that it handles nulls?

This code works for me. The real trick is in the imlementation of the Equals method. Also keep in mind the null check in the GetHashCode.
static void Main(string[] args)
{
IEnumerable<Decimal?> x = new List<Decimal?> { 1.51m, 3, null };
IEnumerable<Decimal?> y = new List<Decimal?> { 1.6m, 3, null };
Assert.Equal(x, y, new DecimalToleranceEqualityComparer(0.1m));
}
public class DecimalToleranceEqualityComparer : IEqualityComparer<Decimal?>
{
private readonly Decimal _tolerance;
public DecimalToleranceEqualityComparer(Decimal tolerance)
{
_tolerance = tolerance;
}
public Boolean Equals(Decimal? x, Decimal? y)
{
if (!x.HasValue && !y.HasValue)
{
// Both null -> they are equal
return true;
}
else if (!x.HasValue || !y.HasValue)
{
// One is null, other is not null -> not equal
return false;
}
else
{
// both have values -> run the actual comparison
return Math.Abs(x.Value - y.Value) <= _tolerance;
}
}
public Int32 GetHashCode(Decimal? obj)
{
if (obj.HasValue)
{
return obj.GetHashCode();
}
else
{
// Here decide what you need
return string.Empty.GetHashCode();
}
}
}

One option that comes to mind could be implementing new equality comparer for nullable decimal type IEqualityComparer<decimal?> which could use your existing DecimalToleranceEqualityComparer internally. Something like
public Boolean Equals(Decimal? x, Decimal? y) {
return (x.HasValue && y.HasValue)?
_decimalToleranceEqualityComparer.Equals(x.Value,y.Value)
: x == y;
}

You are supplying a list of Nullable<decimal>'s and your IEqualityComparer is expecting a list of Decimal's.
With a rewrite like this you should be fine:
public class DecimalToleranceEqualityComparer : IEqualityComparer<decimal?>
{
private readonly decimal _tolerance;
public DecimalToleranceEqualityComparer(decimal tolerance)
{
_tolerance = tolerance;
}
public bool Equals(decimal? x, decimal? y)
{
if (!x.HasValue && !y.HasValue) return true;
if (!x.HasValue || !y.HasValue) return false;
return Math.Abs(x.Value - y.Value) <= _tolerance;
}
public int GetHashCode(decimal? obj)
{
return obj.GetHashCode();
}
}

Related

C# Dictionary that uses an Unordered Pair as its Key?

I'm trying to create a Dictionary is C# that takes an Unordered Pair of Indices as its Key.
For example:
exampleDictionary[new UnorderedPair(x,y)] and exampleDictionary[new UnorderedPair(y,x)] should both return the same value.
Is there a way to create a custom unordered collection other than using a HashSet? Or some way to create an unordered Tuple?
This question is similar to what I'm trying to accomplish, except in C# rather than python.
If the type is not your own or you can't or don't want to modify refer to Theodor Zoulias's answer
Otherwise, assuming that UnorderedPair is your own class you can modify what you could do is e.g.
[Serializable]
public class UnorderedPair<T> : IEquatable<UnorderedPair<T>>
{
public T X;
public T Y;
public UnorderedPair()
{
}
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair<T> other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return X.Equals(other.X) && Y.Equals(other.Y) || X.Equals(other.Y) && Y.Equals(other.X);
}
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((UnorderedPair<T>)obj);
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
return HashCode.Combine(Math.Min(hashX,hashY), Math.Max(hashX,hashY));
}
public static bool operator ==(UnorderedPair<T> left, UnorderedPair<T> right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<T> left, UnorderedPair<T> right)
{
return !Equals(left, right);
}
}
and then e.g.
var testDict = new Dictionary<UnorderedPair<int>, string>();
testDict.Add(new UnorderedPair<int>(1,2), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair<int>(2,1)]);
As per suggestion by Jodrell in the comments you could even make the types swappable - not sure this would be ever needed - but this way you could even have a pair of different types:
[Serializable]
public class UnorderedPair<TX, TY> : IEquatable<UnorderedPair<TX, TY>>
{
public TX X;
public TY Y;
public UnorderedPair()
{
}
public UnorderedPair(TX x, TY y)
{
X = x;
Y = y;
}
public UnorderedPair(TY y, TX x)
{
X = x;
Y = y;
}
public override int GetHashCode()
{
// and for the HashCode (used as key in HashSet and Dictionary) simply order them by size an hash them again ^^
var hashX = X == null ? 0 : X.GetHashCode();
var hashY = Y == null ? 0 : Y.GetHashCode();
var combine = HashCode.Combine(Math.Min(hashX, hashY), Math.Max(hashX, hashY));
return combine;
}
public bool Equals(UnorderedPair<TX, TY> other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
if (typeof(TX) != typeof(TY))
{
return EqualityComparer<TX>.Default.Equals(X, other.X) && EqualityComparer<TY>.Default.Equals(Y, other.Y);
}
return EqualityComparer<TX>.Default.Equals(X, other.X) && EqualityComparer<TY>.Default.Equals(Y, other.Y)
|| X.Equals(other.Y) && Y.Equals(other.X);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return obj switch
{
UnorderedPair<TX, TY> other => Equals(other),
UnorderedPair<TY, TX> otherSwapped => Equals(otherSwapped),
_ => false
};
}
public static bool operator ==(UnorderedPair<TX, TY> left, UnorderedPair<TX, TY> right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<TX, TY> left, UnorderedPair<TX, TY> right)
{
return !Equals(left, right);
}
public static implicit operator UnorderedPair<TX, TY>(UnorderedPair<TY, TX> pair)
{
return new UnorderedPair<TX, TY>(pair.Y, pair.X);
}
}
and
var testDict = new Dictionary<UnorderedPair<int, double>, string>();
testDict.Add(new UnorderedPair<int, double>(1,2.5), "Hello World!");
Console.WriteLine(testDict[new UnorderedPair<double,int>(2.5,1)]);
(.NET Fiddle for both)
You could write a custom IEqualityComparer<UnorderedPair<T>> implementation, and pass it as argument to the constructor of your Dictionary<UnorderedPair<TKey>, TValue>. This way you won't have to modify your UnorderedPair<T> type, by overriding its Equals and GetHashCode methods. Below is an example of such a comparer for the ValueTuple<T1, T2> struct, with both T1 and T2 being the same type:
class UnorderedValueTupleEqualityComparer<T> : IEqualityComparer<(T, T)>
{
private readonly IEqualityComparer<T> _comparer;
public UnorderedValueTupleEqualityComparer(IEqualityComparer<T> comparer = default)
{
_comparer = comparer ?? EqualityComparer<T>.Default;
}
public bool Equals((T, T) x, (T, T) y)
{
if (_comparer.Equals(x.Item1, y.Item1)
&& _comparer.Equals(x.Item2, y.Item2)) return true;
if (_comparer.Equals(x.Item1, y.Item2)
&& _comparer.Equals(x.Item2, y.Item1)) return true;
return false;
}
public int GetHashCode((T, T) obj)
{
int h1 = _comparer.GetHashCode(obj.Item1);
int h2 = _comparer.GetHashCode(obj.Item2);
if (h1 > h2) (h1, h2) = (h2, h1);
return HashCode.Combine(h1, h2);
}
}
Usage example:
Dictionary<(int, int), string> dictionary = new(
new UnorderedValueTupleEqualityComparer<int>());
Inspired by #derHugo's answer and my comments on it,
Fiddle here
A generic implementation,
#nullable enable
public class UnorderedPair<T> : IEquatable<UnorderedPair<T>>
{
private static IEqualityComparer<T> comparer = EqualityComparer<T>.Default;
public T X { get; }
public T Y { get; }
public UnorderedPair(T x, T y)
{
X = x;
Y = y;
}
public bool Equals(UnorderedPair<T>? other)
{
if(other is null)
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
// For equality simply include the swapped check
return
comparer.Equals(X, other.X) && comparer.Equals(Y, other.Y)
||
comparer.Equals(X, other.Y) && comparer.Equals(Y, other.X);
}
public override bool Equals(object? obj)
{
return Equals(obj as UnorderedPair<T>);
}
public override int GetHashCode()
{
unchecked
{
return
(X is null ? 0 : comparer.GetHashCode(X))
+
(Y is null ? 0 : comparer.GetHashCode(Y));
}
}
public static bool operator ==(UnorderedPair<T>? left, UnorderedPair<T>? right)
{
return Equals(left, right);
}
public static bool operator !=(UnorderedPair<T>? left, UnorderedPair<T>? right)
{
return !Equals(left, right);
}
}
#nullable disable

Breakpoint in Equals implimentation on IEqualityComparer<CustomClass> is never hit

I have a simple custom class Point:
public class Point: IEqualityComparer<Point>
{
public double X;
public double Y;
public double Z;
private double[] startPointCoords;
public Point()
{
}
public Point(double[] pointArray)
{
this.startPointCoords = pointArray;
X = pointArray[0];
Y = pointArray[1];
Z = pointArray[2];
}
public bool Equals(Point x, Point y)
{
if(x.X == y.X && x.Y == y.Y && x.Z == y.Z)
{
return true;
}
return false;
}
public int GetHashCode(Point obj)
{
string xString = X.ToString().Replace(".", "");
string yString = Y.ToString().Replace(".", "");
string zString = Z.ToString().Replace(".", "");
int xInt = Convert.ToInt32(xString);
int yInt = Convert.ToInt32(yString);
int zInt = Convert.ToInt32(zString);
return xInt - yInt + zInt;
}
}
I am using this class in a Dictionary. I am checking for if the point instance has been added to the dictionary using:
if (!pointsToProcess.ContainsKey(startPoint))
{
pointsToProcess.Add(startPoint, startPoint);
}
I am debugging my code to make sure Equals is working correctly. My break point I have set in Point.Equals is never hit. I set a break point in Point.GetHashCode and it is never hit either. It seems like they are not being used.
I know that there are classes called Point in .Net. I am absolutely sure that all the Point that I have in my code is from my custom namespace.
Why would my Point.Equals and Point.GetHashCode not be reached when setting a break point?
The Equals(a, b) method is not hit by IEquatable, so you'll need to tailor it to suit the interface.
Try this one:
public class Point : IEquatable<Point>
{
public double X;
public double Y;
public double Z;
private double[] startPointCoords;
public Point()
{
}
public Point(double[] pointArray)
{
this.startPointCoords = pointArray;
X = pointArray[0];
Y = pointArray[1];
Z = pointArray[2];
}
public override bool Equals(object obj) => Equals(obj as Point);
public bool Equals(Point other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return this.X == other.X &&
this.Y == other.Y &&
this.Z == other.Z;
}
public override int GetHashCode()
{
string xString = X.ToString().Replace(".", "");
string yString = Y.ToString().Replace(".", "");
string zString = Z.ToString().Replace(".", "");
int xInt = Convert.ToInt32(xString);
int yInt = Convert.ToInt32(yString);
int zInt = Convert.ToInt32(zString);
return xInt - yInt + zInt;
}
}
Also there are a lot of ways to implement hashcodes in C# for custom objects. While not perfect, one simple way would be using anonymous object hashing:
public override int GetHashCode()
{
return new { X, Y, Z }.GetHashCode();
}

How to sort List<T> in c#

I've got a List<Card>, and I want to sort these cards
So, I'm looking for a method to sort them with different criterias, like their ID, their Name ...
public class Card : IComparer
{
public string ID;
public string Name;
public int CompareId(object firstCard, object secondCard)
{
Card c1 = (Card)firstCard;
Card c2 = (Card)secondCard;
return c1.Id.CompareTo(c2.Id);
}
}
But then, visual studio sent me an error :
'Card' does not implement interface member 'IComparer<Card>.Compare(Card, Card)'
You, probably, want to have your class Comparable not a Comparator
public class Card : IComparable<Card>
{
public string ID;
public string Name;
public int CompareTo(Card other)
{
if (null == other)
return 1;
// string.Compare is safe when Id is null
return string.Compare(this.Id, other.Id);
}
}
then
List<Card> myList = ...
myList.Sort();
Edit: If you want to have several criteria to choose from, you have to implement several Comparers as separated classes, e.g.
public sealed class CardByIdComparer : IComparer<Card>
{
public int Compare(Card x, Card y)
{
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
else
return string.Compare(x.Id, y.Id);
}
}
and when sorting provide the required:
List<Card> myList = ...
myList.Sort(new CardByIdComparer());
Edit 2: (inspired by spender's library). If you want to combine several comparers into one (i.e. use comparer1, on tie - comparer2 etc.)
public sealed class ComparerCombined<T> : IComparer<T> {
private IComparer<T>[] m_Comparers;
public ComparerCombined(params IComparer<T>[] comparers) {
if (null == comparers)
throw new ArgumentNullException(nameof(comparers));
m_Comparers = comparers
.Select(item => item == null ? Comparer<T>.Default : item)
.Where(item => item != null)
.Distinct()
.ToArray();
}
public int Compare(T x, T y) {
if (object.ReferenceEquals(x, y))
return 0;
else if (null == x)
return -1;
else if (null == y)
return 1;
foreach (var comparer in m_Comparers) {
int result = comparer.Compare(x, y);
if (result != 0)
return result;
}
return 0;
}
}
usage:
myList.Sort(new ComparerCombined(
new CardByIdComparer(), // Sort By Id
new CardByNameComparer() // On tie (equal Id's) sort by name
));
The easiest way You can use Linq:
List<Card> objSortedList = objListObject.OrderBy(o=>o.ID).ToList();
or
List<Card> objSortedList = objListObject.OrderByDescending(o=>o.ID).ToList();
Good examples for demonstrate the concept of
List<T>.Sort(IComparer <T>) method check the link please.
IComparer<T> in this example compare method used for strings IComparer<T>
but you can use this for ID(int) too.
using System;
using System.Collections.Generic;
class GFG : IComparer<string>
{
public int Compare(string x, string y)
{
if (x == null || y == null)
{
return 0;
}
// "CompareTo()" method
return x.CompareTo(y);
}
}
public class geek
{
public static void Main()
{
List<string> list1 = new List<string>();
// list elements
list1.Add("C++");
list1.Add("Java");
list1.Add("C");
list1.Add("Python");
list1.Add("HTML");
list1.Add("CSS");
list1.Add("Scala");
list1.Add("Ruby");
list1.Add("Perl");
int range = 4;
GFG gg = new GFG();
Console.WriteLine("\nSort a range with comparer:");
// sort the list within a
// range of index 1 to 4
// where range = 4
list1.Sort(1, range, gg);
Console.WriteLine("\nBinarySearch and Insert Dart");
// Binary Search and storing
// index value to "index"
int index = list1.BinarySearch(0, range,
"Dart", gg);
if (index < 0)
{
list1.Insert(~index, "Dart");
range++;
}
}
}
You need to implement IComparer
public int Compare(Card card1, Card card2)
{
if (card1.ID > card2.ID)
return 1; //move card1 up
if (card2.ID < card1.ID)
return -1; //move card2 up
return 0; //do nothing
}

How do you create your own DataType?

I would like to create my own DataType called positiveInteger.
I know what you are thinking?
You are thinking that I should use uint here.
But uint contains 0 and I want only positive numbers.
Now you may tell me that Create a class called positiveInteger.
Yes, I can create a Class called positiveIntegerbut I don't know how to implement that class such that this new DataType accepts only positive integer values?
If you want to be able to "accept" values, which are (mostly) compiled as constant int values, then you'll need to implement an implicit conversion to positiveInteger from int
public class positiveInteger
{
public static implicit operator positiveInteger(int source)
{
if(source <= 0) throw new ArgumentOutOfRangeException();
}
}
This will allow you to assign a positiveInteger like so
positiveInteger number = 5;
It will also, however, make it possible to assign an int value
int i = 5;
positiveInteger number = i; // This will throw an exception when i <= 0
An example imlementation could be:
public struct PositiveInteger : IEquatable<PositiveInteger>, IComparable<PositiveInteger>
{
public PositiveInteger(uint value)
{
if (value <= 0) throw new ArgumentOutOfRangeException();
_value = value;
}
public uint Value { get { return _value == 0 ? 1 : _value; } }
private readonly uint _value;
public static implicit operator PositiveInteger(uint value)
{
return new PositiveInteger(value);
}
public static implicit operator uint(PositiveInteger value)
{
return value.Value;
}
public static PositiveInteger operator +(PositiveInteger value1, PositiveInteger value2)
{
var result = value1.Value + value2.Value;
if (result < value1.Value || result < value2.Value)
{
throw new ArgumentOutOfRangeException(); //overflow
}
return result;
}
public static PositiveInteger operator -(PositiveInteger value1, PositiveInteger value2)
{
if (value1.Value < value2.Value) throw new ArgumentOutOfRangeException();
return value1.Value - value2.Value;
}
public override bool Equals(object obj)
{
if (obj is PositiveInteger == false) return false;
return Equals((PositiveInteger)obj);
}
public bool Equals(PositiveInteger other)
{
return Value == other.Value;
}
public override int GetHashCode()
{
return (int)Value;
}
public int CompareTo(PositiveInteger other)
{
if (Value == other.Value) return 0;
return Value < other.Value ? -1 : 1;
}
public override string ToString()
{
return Value.ToString(CultureInfo.InvariantCulture);
}
}
And a small test:
void Test()
{
var list = new List<PositiveInteger> {5, 1, 3};
list.Sort(); // 1,3,5
var a = new PositiveInteger(1);
var b = new PositiveInteger(2);
var c = a + b; // = 3
var d = c - b; // = 1
var e = d - a; // throws ArgumentOutOfRangeException
}
Try this for your constructor. But it is still not a good idea because you should still validate all data before you use it. Just like checking that a denominator is not zero before blindly dividing.
public class PositiveInteger {
private uint _value;
public PositiveInteger(int x) {
if (x < 1) {
throw new Exception("Invalid value. Value is not positive.");
}
_value = x;
}
}

Implementing IComparer combining multiple Linq OrderBy's

My problem is that I always want to order a collection of objects in a certain fashion.
For example:
class foo{
public string name {get;set;}
public DateTime date {get;set;}
public int counter {get;set;}
}
...
IEnumerable<foo> dosomething(foo[] bar){
return bar.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
}
The issue I have is its quite longwinded tacking-on the sort order all the time. A neat solution appears to just create a class that implements IComparer<foo>, meaning I can do:
IEnumerable<foo> dosomething(foo[] bar){
return bar.OrderBy(a=>a, new fooIComparer())
}
.
The problem is, the order method this implements is as follows
...
public int Compare(foo x, foo y){ }
Meaning it compares on a very granular basis.
The currently implementation (which will probably work, although im writing pseudocode)
public int Compare(foo x, foo y){
if (x==y)
return 0;
var order = new []{x,y}.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
return (order[0] == x) ? -1 : -1;//if x is first in array it is less than y, else it is greater
}
This is not exactly efficient, can another offer a neater solution? Ideally without a Compare(x,y) method altogether?
Option 1 - The Comparer
As you're ordering by multiple conditions, you'll to check them individually within each case; for example, if x.name and y.name are equal, then you would check x.date and y.date, and so on.
public class FooComparer : IComparer<Foo>
{
public int Compare(Foo x, Foo y)
{
// nasty null checks!
if (x == null || y == null)
{
return x == y ? 0
: x == null ? -1
: 1;
}
// if the names are different, compare by name
if (!string.Equals(x.Name, y.Name))
{
return string.Compare(x.Name, y.Name);
}
// if the dates are different, compare by date
if (!DateTime.Equals(x.Date, y.Date))
{
return DateTime.Compare(x.Date, y.Date);
}
// finally compare by the counter
return x.Counter.CompareTo(y.Counter);
}
}
Option 2 - The extension method
An alternative, not so appealing approach, could be an extension method. Sadly as the TKey for each ThenBy can be different, we lose the power of generics, but can safely replace it with the type object in this case.
public static IOrderedEnumerable<T> OrderByThen<T>(this IEnumerable<T> source, Func<T, object> selector, params Func<T, object>[] thenBySelectors)
{
IOrderedEnumerable<T> ordered = source.OrderBy(selector);
foreach (Func<T, object> thenBy in thenBySelectors)
{
ordered = ordered.ThenBy(thenBy);
}
return ordered;
}
You have to implement IComparable<foo> and compare all properties:
class foo: IComparable<foo>, IComparer<foo>
{
public string name { get; set; }
public DateTime date { get; set; }
public int counter { get; set; }
public int Compare(foo x, foo y)
{
if (x == null || y == null) return int.MinValue;
if (x.name != y.name)
return StringComparer.CurrentCulture.Compare(x.name, y.name);
else if (x.date != y.date)
return x.date.CompareTo(y.date);
else if (x.counter != y.counter)
return x.counter.CompareTo(y.counter);
else
return 0;
}
public int CompareTo(foo other)
{
return Compare(this, other);
}
}
Then you can use OrderBy in this way:
var ordered = foos.OrderBy(f => f).ToList();
what's wrong with an extension method?
Why wont you simply compare your values:
int Compare(foo x, foo y)
{
if (x== null && y == null)
return 0;
else if (x == null)
return -1;
else if (y == null)
return 1;
var nameComparision = string.Compare(x.name,y.name);
if (nameComparision != 0)
return nameComparision;
var dateComparision = x.date.CompareTo(y.date);
if (dateComparision != 0)
return dateComparision;
var counterComparision = x.counter.CompareTo(y.counter);
return counterComparision;
}

Categories

Resources