Reputation: 755
I am trying to merge two nested lists. I have a LatLng class which contains two properties, Latitude and Longitude - both are doubles. This class is brought in from a NuGet package called DotNetCoords. A line is represented as an array of LatLngs, and I have a number lines in a network that are represented below:
IEnumerable<LatLng[]> lineNetwork;
I have another network of lines that I want to add to this one but exclude duplicates, currently I have tried distinct but I don't think LatLng is equatable so this doesn't seem to work.
I've also tried the below which doesn't work either.
IEnumerable<LatLng[]> lineNetwork;
IEnumerable<LatLng[]> newLineNetwork;
lineNetwork.AddRange(newLineNetwork.Where(x => !lineNetwork.Contains(x)));
It would need to check that ALL coordinates in the line are not equal before adding it. So for example:
LatLng[] line1 = {
new LatLng(Latitude: 0.1, Longitude: 0.1),
new LatLng(Latitude: 0.2, Longitude: 0.2),
new LatLng(Latitude: 0.3, Longitude: 0.3)
}
LatLng[] line2 = {
new LatLng(Latitude: 0.1, Longitude: 0.1),
new LatLng(Latitude: 0.2, Longitude: 0.2),
new LatLng(Latitude: 0.3, Longitude: 0.3)
}
LatLng[] line3 = {
new LatLng(Latitude: 0.1, Longitude: 0.1),
new LatLng(Latitude: 0.4, Longitude: 0.4),
new LatLng(Latitude: 0.5, Longitude: 0.5)
}
Where Line1 & Line3 would be added but not Line2 as all coordinates are shared with Line1.
Upvotes: 0
Views: 95
Reputation: 37000
One way would be to implement IEqualityComparer
in order to compare two instances of LatLng
and than use IEnumerabe<T>.Distinct
. However as you have an array of that type, you also need the comparer to work on that array-type:
class MyComparer : IEqualityComparer<LatLng[]>
{
public bool Equals(LatLng[] x, LatLng[y])
{
return a.SequenceEquals(y, new MyLatLngComparer(0.001));
}
public int GetHashCode(LatLng[] x)
{
return x.First().GetHashCode();
}
}
As the LatLng
-class has no own implementation for equality, you have to provide your own within SequenceEquals
:
class MyLatLngComparer : IEqualityComparer<LatLng>
{
private readonly double Tolerance;
public MyLatLngComparer(double tolerance) { this.Tolerance = tolerance ;}
public bool Equals(LatLng x, LatLng y)
{
return Math.Abs(x - y) <= Tolerance;
}
public int GetHashCode(LatLng x)
{
return x.GetHashCode();
}
}
Here you see how to compare floating-point values. Equality for those types should allways be implemented by some small tolerance as shown above, in order to avoid rounding-issues.
Finally you can use Distinct
with the provided comparer:
lineNetwork.AddRange(newLineNetwork.Distinct(new MyComparer()));
I also omitted some null-checks for brevity. You should in particular check if your array even has an element, e.g. within MyComparer.GetHashCode
.
Upvotes: 2
Reputation: 1339
Three possible options:
LatLng
and override Equals and GetHashCodeIEquatable<LatLng>
as mentioned before (similar to the previous option, but more efficient)Contains
operation (your code fails because the Contains
tries to search the equality based on the object reference. The only way is to override the Equals method)Upvotes: -1