简体   繁体   中英

Linq join without equals

I have a list of rectangles and a list of points. I want to construct a LINQ query that will match the list of points with their corresponding rectangles. Something like this:

// Does not compile
var matches = from rect in rectangles
              join point in points on rect.Contains(point)
              select new { rect, point };

How does one accomplish something like this with LINQ?

EDIT:

My lists are equal size - I have one point to match with one rectangle, and the rectangles don't overlap.

However, the point of the question isn't so much to solve this one specific problem. I'm interested, in general, how to join two lists on any condition other than simply 'equals'.

You can use multiple from clauses to achieve a join

 var matches = from p in points
               from r in rectangles
               where r.Contains(p)
               select new { r, p };

Multiple from clauses are more flexible than the join syntax ( see myth 5 of 10 LINQ myths ). You need to learn only this one and all joins will be easy.

You can use Enumerable.ToLookup to create a lookup table per rectangle:

var lookup = points.ToLookup(p => rectangles.First(r => r.Contains(point)));

Using this is similar to a grouping query:

foreach(var group in lookup)
{
    Console.WriteLine("Rectangle {0} contains:", group.Key);
    foreach(var point in group)
        Console.WriteLine("    {0}", point);
}

On a side note - this query is quadratic in nature, and likely to perform poorly with very large datasets. If you need to do this for many points and/or many rectangles, you may wish to investigate spatial data structures for quicker lookups. That may not be an issue in this case, however.

Have you tried just using a where statement, instead of joining them:

var matches = from rectangle in rectangles
              from point in points
              where rectangle.Contains(point)
              select new { rectangle, point };

There are two ways of getting what you want.

points.Select(p => new { Point = p, Rectangles = rectangles.Where(r => r.Contains(p) });

This caters to the case where a point may be in many rectangles.

points.Select(p => new { Point = p, Rectangle = rectangles.First(r => r.Contains(p) });

This caters to the case where a point is in exactly one rectangle.

The second case should work best in your scenario.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM