I'm going to use Range
instead of "custom structure":
class Range
{
public Range(int from, int to)
{
From = from;
To = to;
}
public int From { get; }
public int To { get; }
}
using IEqualityComparer, but it seems can't work well.
Maybe because "equality" can't trivially be defined by equating one (or both) Range
properties? But you (almost) perfectly define equality...
x.From == y.To && x.To == y.From
I think this should be amended by...
x.From == y.From && x.To == y.To
It seems reasonable that two ranges having equal To
and From
are equal.
This would be enough to implement an IEqualityComparer
's Equals
method.
However, the challenge of implementing GetHashCode
is always that it should match the Equals
method --equality defined there should result in identical hashes-- but now based on the properties of one object instance.
The first impulse is to base the hash on From + To
. But that would make range(8,5)
equal to range(7,6)
. This can be solved by also bringing From - To
into the equation. Two ranges are equal when From + To
is equal and when the absolute difference From - To
is equal:
x.From + x.To == y.From + y.To
&& Math.Abs( x.From - x.To) == Math.Abs(y.From - y.To);
This is equality based on properties of a single instance on both sides of the equations so now we can implement GetHashCode
. Following best practices (and helped by Resharper):
public int GetHashCode(Range obj)
{
var hashCode = -1781160927;
hashCode = hashCode * -1521134295 + (obj.From + obj.To).GetHashCode();
hashCode = hashCode * -1521134295 + (Math.Abs(obj.From - obj.To)).GetHashCode();
return hashCode;
}
And the complete comparer:
class RangeEqualityComparer : IEqualityComparer<Range>
{
public bool Equals(Range x, Range y)
{
return y != null
&& x != null
&& x.From + x.To == y.From + y.To
&& Math.Abs( x.From - x.To) == Math.Abs(y.From - y.To);
}
public int GetHashCode(Range obj)
{
var hashCode = -1781160927;
hashCode = hashCode * -1521134295 + (obj.From + obj.To).GetHashCode();
hashCode = hashCode * -1521134295 + (Math.Abs(obj.From - obj.To)).GetHashCode();
return hashCode;
}
}
Now you get distinct ranges by...
ranges.OrderBy(r => r.From).Distinct(new RangeEqualityComparer())
The ordering defines which range of "equal" ranges will appear in the end result.