Zodiac Zodiac - 2 months ago 9
C# Question

C# LINQ - how to check if all elem satisfy condition

I have a list of object. Those objects contain lat and lon coordinates among other properties. I want to extract only thoose coordonates that satisfy

Math.Abs(elem1.lat - elem2.lat) < Delta && Math.Abs(elem1.lon - elem2.lon) < Delta


Basically I want to extract only objects that dont have coordinates close to each other.

Is there an elegant way to do this with C# LINQ ?

EDIT :

So elem1 and elem2 are any 2 elements from the list. I want all elements that will result from the query to have more then delta between any of them

Answer Source

Your question is not well-phrased. If your elem1 or elem2 is a single item you want to compare against, this is pretty easy. Just do a LINQ Where to find the items that match the criteria.

Coordinates it = new Coordinates { lat = 5, lon = 5 };
var picked = list.Where(x => Math.Abs(x.lat - it.lat) < Delta && Math.Abs(x.lon - it.lon) < Delta);

If, on the other hand, your two elements are any two elements in the list, then you run into a problem where you need to clarify things. As I mentioned in my comment, if your have elem1, elem2, and elem3, and if the first two don't match the criteria but if elem2 and elem3 do match the criteria, then are both of those elements (2 and 3) 'hits'? Assuming they are, the solution below will work. I wrote a simple Comparer to help make things easy as well.

public class CoordinateComparer : IEqualityComparer<Coordinates>
{
    public bool Equals(Coordinates x, Coordinates y)
    {
        return (x.lat == y.lat && x.lon == y.lon);
    }

    public int GetHashCode(Coordinates obj)
    {
        unchecked
        {
            int hash = 17;
            if (obj != null)
            {
                hash = hash * 23 + obj.lat.GetHashCode();
                hash = hash * 23 + obj.lon.GetHashCode();
            }
            return hash;
        }
    }
}

public class Coordinates
{
    public int lat { get; set; }
    public int lon { get; set; }
}

class MainClass
{
    public static void Main(string[] args)
    {
        List<Coordinates> list = new List<Coordinates>();
        list.Add(new Coordinates { lat = 5, lon = 4 }); 
        list.Add(new Coordinates { lat = 4, lon = 5 });
        list.Add(new Coordinates { lat = 7, lon = 4 });
        list.Add(new Coordinates { lat = 6, lon = 3 });
        list.Add(new Coordinates { lat = 8, lon = 2 });

        double Delta = 1.1;

        List<Coordinates> results = new List<Coordinates>();
        foreach(Coordinates item in list)
        {
            // Find items that match the condition
            var matches = list.Where(x => Math.Abs(x.lat - item.lat) < Delta && Math.Abs(x.lon - item.lon) < Delta);
            // The 'item' will always match the condition with itself, which is undesirable, so remove it
            matches = matches.Except(new List<Coordinates> { item }, new CoordinateComparer());
            // Add the items that are not already in it to the results list
            results.AddRange(matches.Where(x => !results.Contains(x, new CoordinateComparer())));
        }

        foreach (Coordinates item in results)
            Console.WriteLine("Lat : {0}, Lon : {1}", item.lat, item.lon);

        Console.ReadLine();
    }   
}

Bear in mind that if your latitude and longitude coordinates are either double or float, which they probably are, you might run into problems when comparing. But that's an altogether different question.