How to compare each element with every other in LINQ? - c#

The problem is, for an IEnumerable of rectangles I need to know for each rectangle whether it intersects any of the others. So the output would be a sequence with the same count containing one boolean value for every rectangle.
So it is kind of a nested loop problem but it should not compare the element with itself and if rect A intersects rect B there is no need to check rect B again.
Is this possible with LINQ?

//for every rectangle in your list.
//search the list, eliminating the same one,
//and find if any of the others intersect with it.
var intersectingRectangles = rectangles
.Where(rect => rectangles
.Where(r => r != rect)
.Any(r => DoIntersect(rect, r)));
This assumes that you have a function DoIntersect taking two rectangles and telling you if they intersect.
There are MANY improvements that could be made to this if it is not effecient enough for your needs. you could potentially sort based on coordinates and be able to eliminate a lot of comparisons. But, that all depends on what your data looks like and how fast this needs to be.
If you want to have the output contain each of the original IEnumerable along with a status about whether is intersects any others than switch to using a Select() as below.
var allWithIntersectionStatus = rectangles.Select(rect => new
{
Value = rect,
DoesIntersect = rectangles
.Where(rectWhere => rectWhere != rect)
.Any(rectAnt => DoIntersect(rect, rectAnt))
});

Related

Using LINQ on shapes

I'd like to ask if it is possible to use LINQ to draw rectangles saved in list with a condition of drawing a specific rectangle inside that list. This is a newbie question, so please bear with me.
As the name says, LINQ is for querying any kind of data. As long as you see objets as data carriers you can use LINQ to query them. But it can't do any drawing or any kind of code execution. If you divide your problem into two parts; first selecting the appropriate shapes and then drawing them, the code could be something like this:
public void QueryShapes(IEnumerable<Shape> shapes)
{
var rectangles =
from shape in shapes
where shape is Rectangle
let rect = (Rectangle)shape
where rect.Width > 100 // conditions...
select shape as Rectangle;
rectangles.ToList().ForEach(Draw);
}
public void Draw(Rectangle rectangle)
{
// drawing
}

Ensure two random shapes are never near each other or overlapping

var random = new Random();
Canvas.SetLeft(rectangle, random.Next((int)(ImageCanvas.Width - 100)));
Canvas.SetTop(rectangle, random.Next((int)(ImageCanvas.Height - 100)));
return rectangle;
So the above code just randomly sets the Top and Left positions of a rectangle that will appear on the canvas. I can easily reuse this code if I want multiple rectangles to appear on the screen, however what I was having trouble doing is tweaking the code so that each rectangle is never overlapping each other.
I thought of maybe doing a while loop that keeps running random.Next((int)(ImageCanvas.Height - 100)) continuously until it is not equal to the previous random... But that isn't perfect. The shapes are quite big, so having slightly different X or Y coordinates doesn't prevent a overlap. They would somehow need to be at least 50 pixels distance between each other or something for this to prevent any overlap between other rectangles.
Assuming your Canvas is reasonably large, i.e. the rectangles will not occupy a large amount of the area, it most likely suffices to simply generate rectangles at random (as in your example code), and then check to make sure they don't overlap with any of the previously selected rectangles.
Note that "overlaps with another rectangle" is really the same as "has a non-empty intersection with another rectangle". And .NET provides that functionality; for WPF, you should use the System.Windows.Rect struct. It even has an IntersectsWith() method, giving the information you need in a single call (otherwise you'd have to get the intersection as one step, and then check to see if the result is empty in a second step).
The whole thing might look something like this:
List<Rectangle> GenerateRectangles(Canvas canvas, int count, Size size)
{
Random random = new Random();
List<Rect> rectangles = new List<Rect>(count);
while (count-- > 0)
{
Rect rect;
do
{
rect = new Rect(random.Next((int)(canvas.Width - size.Width),
(int)(canvas.Height - size.Height), size.Width, size.Height);
} while (rectangles.Any(r => r.IntersectsWith(rect));
rectangles.Add(rect);
}
return rectangles.Select(r =>
{
Rectangle rectangle = new Rectangle();
rectangle.Width = r.Width;
rectangle.Height = r.Height;
canvas.SetLeft(rectangle, r.Left);
canvas.SetTop(rectangle, r.Top);
return rectangle;
}).ToList();
}
You would want something more sophisticated if you were dealing with a more constrained area and/or a larger number of rectangles. The above won't scale well for large numbers of rectangles, especially if the probability of a collision is high. But for your stated goals, it should work fine.

Comparing elements in an IEnumerable to eachother

Hopefully a quick question. I have an IEnumerable of type Position where Position is defined as follows:
public class Position {
public double Latitude { get; set; }
public double Longitude { get; set; }
}
What I need to do is quickly sort through the IEnumerable to find elements that fall within a certain distance of eachother. The elements in the IEnumerable do not get populated in any specific order, but at any one time I need to be able to compute which of the members of the IEnumerable fall within x kilometers of eachother.
Now, I already have a Haversine implementation and for argument's sake, we can say it's called GetDistance and has the following signature:
double GetDistance(Position one, Position two);
I've got a few ideas, but none of them feel particularly efficient to my mind. To give a little more info, it's unlikely the IEnumerable will ever hold more than 10,000 items at any one time.
What I'd like to arrive at is something, perhaps an extension method, that lets me invoke it to return an IEnumerable which contains just the subset from the original collection which meets the criteria, for example:
OriginalEnumerable.GetMembersCloserThan(kilometers: 100);
Any help, much appreciated.
EDIT: For clarity, consider the IEnumerable I want to search describes a circle with radius r. It's members are coordinates within the circle. I'm trying to determine which members (points) are within a given proximity of eachother.
Something like this? Assuming GetDistance is available.
public static IEnumerable<Position> GetMembersCloserThan(this IEnumerable<Position> positions, double maxDistance)
{
return positions.Where(a => positions.Any(b => a != b && GetDistance(a, b) < maxDistance));
}
Edit I see now you are also interested in performance. The above is not particularly fast, though it is not horribly slow either since the math is pretty simple for comparing distances. Let me know if it satisfies your requirements.
Edit 2 This one is much faster--it won't test against past failures and will automatically add a match to the success list
public static IEnumerable<Position> GetMembersCloserThan(this IEnumerable<Position> positions, double maxDistance)
{
List<Position> closePositions = new List<Position>();
List<Position> testablePositions = positions.ToList();
foreach (Position position in positions)
{
// Skip this one, it has already been matched.
if (closePositions.Contains(position))
continue;
bool isClose = false;
foreach (Position testAgainstPosition in testablePositions)
{
if (position == testAgainstPosition)
continue;
if (GetDistance(position, testAgainstPosition) < maxDistance)
{
// Both the position and the tested position pass.
closePositions.Add(position);
closePositions.Add(testAgainstPosition);
isClose = true;
break;
}
}
// Don't test against this position in the future, it was already checked.
if (!isClose)
{
testablePositions.Remove(position);
}
}
return closePositions;
}
If you need more performance: Put the items in a Lists sorted by latitude.
To calculate your desired set of locations, iterate one of them. But for your distance calculation, you only need to consider the items, that are at most 100km different in latitude. That means, you can go back item by item, until the difference is greater than 100km. You need to wrap around the end of the list, however.
Mark all items (or yyield return) that are closer than 100km and move on.
Although I cannot quantify the expense, the sorting should amortize for large sets. Also, it may perform bad if most point lie at similar latitude. If that is an issue, use a 2D-Dictionary with rounded coordinates as keys.

Given a list of color objects, find the most/least frequently existing color, and return that as a color object

Currently I have a list of Color objects. The format of each are RGB format, which means, for example, 0, 0, 0 would be the color black.
I am interested in looping through this list and do sort-of a histogram of it. Find out the most frequently appearing color and the lowest frequently existing color. A question I have is how do I bin color values? Given a list of doubles, you can bin the values through finding its range, and then dividing it by how many bins you want. Not sure how one could achieve that through Color objects.
A piece of code I have currently have uses LINQ:
var cs = new List<Color> {
Color.White,
Color.Black
};
var max = cs.OrderByDescending(x => (0.2125 * x.R) + (0.7154 * x.G) + (0.0721 * x.B)).Last();
It takes my list of colors, and finds the "max" value of that list, and outputs either White or Black, depending on whether the output is max or min.
All in all I am interested in a .Mode() method for a list of colors.
For calculating mode try ToLookup with ordering:
var ordered = cs.ToLookup(c => c).OrderByDescending(c => c.Count()).First().Key;
Update:
You can create an extension method as well:
public static Color Mode(this IEnumerable<Color> colors)
{
return colors.ToLookup(c => c).OrderByDescending(c => c.Count()).First().Key;
}

Compare points inside a list

I have a simple list of points List<Point> which I populate using the mouse. Like, when I click the mouse, the location is added into the list. The problem is that the new location is added at the bottom of the list. What I want to do is, when I click the mouse, search all the existing points in the list and return the closest point to the mouse location, and insert the new point after that one.
I've been searching the web for hours and I can't seem to find a solution. Any help will be appreciated. Thanks!
The List<> class contains an .Insert() method to do just that. When you search the list and find the "closest" element (however you define that logic), you can get the index of that object in the list. Then just insert the new one after that index. Something like:
var closestPoint = FindClosestPoint(listOfPoints, newPoint);
var index = listOfPoints.IndexOf(closestPoint);
listOfPoints.Insert(index + 1, newPoint);
Getting the closest point itself should be a simple matter of geometry. You have two X/Y coordinates on a plane. The distance between them is the square root of the sum of the squares of the axes. So you just need the element where that value is the smallest. Something like this:
var closestPoint = listOfPoints
.Select(p => new {
Point = p,
Distance = Math.Sqrt(
Math.Pow(Math.Abs(p.X - closestPoint.X), 2) +
Math.Pow(Math.Abs(p.Y - closestPoint.Y), 2)
)
})
.OrderByDesc(p => p.Distance)
.First();

Categories