I have a problem. I'm finding an intersection using the code below:
Envelope[][] extents = new Envelope[tilesCountX][tilesCountY];
// some code here to set "extents" values
var intersectedTiles =
extents
.SelectMany(es => es)
.Where(e => EnvIntersects(e, bounds))
.ToList();
private static bool EnvIntersects(Envelope e1, Envelope e2)
{
return e1.MinX >= e2.MinX && e1.MaxX <= e2.MaxX && e1.MinY >= e2.MinY && e1.MaxY <= e2.MaxY;
}
It works but I want to get the indexes of intersected extents.
e.g.
If extents[2][7] is an intersected element, I want to get 2 and 7.
Is it possible by modifying my code?
[edit]
bounds is an Envelope that has MinX, MinY, MaxX and MaxY properties inside.
Envelope bounds = new Envelope();
bounds.MinX = some_value_1;
bounds.MaxX = some_value_2;
bounds.MinY = some_value_3;
bounds.MaxY = some_value_4;
I think this could also give you what you want:
var intersectedIndices =
from x in Enumerable.Range(0, tilesCountX)
from y in Enumerable.Range(0, tilesCountY)
where EnvIntersects(extents[x, y], bounds)
select new { x, y };
Try this:
var intersectedTiles =
extents
.SelectMany((es,i) => es.Select((x,j)=>new{I=i,J=j,Element = x}))
.Where(e => EnvIntersects(e.Element, bounds))
.ToList();
where for all elements in intersectedTiles this is true:
intersectedTiles[x].Element == extents[intersectedTiles[x].I][intersectedTiles[x].J]
Related
I have a two dimensional array containing objects of type MyObj.
private MyObj[,] myObjs = new MyObj[maxX, maxY];
I want to get the indices from the array when passing in a matching object. I want to get the x and y value from this array. I can return these two values as a Position object that takes a x and y coordinate.
private Position GetIndices(MyObj obj)
{
for (int x = 0; x < myObjs.GetLength(0); x++)
{
for (int y = 0; y < myObjs.GetLength(1); y++)
{
if (myObjs[x, y] == obj)
{
return new Position(x, y);
}
}
}
}
Is it possible to get this code shorten to some Linq code lines?
But I don't think, it looks nice :)
var result = Enumerable.Range(0, myObjs.GetLength(0))
.Select(x => Enumerable.Range(0, myObjs.GetLength(1)).Select(y => new { x, y }))
.SelectMany(o => o)
.FirstOrDefault(o => myObjs[o.x, o.y] == obj);
Here's another option, if you're interested. It uses an indexer inside the first select and does a little math to find where that index falls inside the two-dimensional array.
var o = new MyObj();
myObjs[1,2] = o;
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 / myObjs.GetLength(1), x.Item2 % myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=1,Y=2}
It sorta looks like you're considering the x-coordinate to be the height of the array, and the y-coordinate the width, in which case you'd want to switch it up slightly:
var p = myObjs.Cast<MyObj>()
.Select((x,i) => Tuple.Create(x,i))
.Where(x => x.Item1 == o)
.Select(x => new Point(x.Item2 % myObjs.GetLength(1), x.Item2 / myObjs.GetLength(1)))
.SingleOrDefault();
Console.WriteLine(p); // prints {X=2,Y=1}
I used Point instead of Position since it's built into .NET but you should be able to just swap one for the other.
Yes, that is possible. You can ask Resharper to do the job (loop to linq) for you. After installation, just use the feature.
I have an array of Vector3's from the polygon collider. I want to have an array of the indexes of all the Vector3's that are higher then a certain y.
private void Awake()
{
polygonCollider2D = GetComponent<PolygonCollider2D>();
float lowestY = polygonCollider2D.points.Min(x => x.y);
// So in the sentence below I want to have a array of indexes instead of a array of vector3's.
topPoints = polygonCollider2D.points.Where(x => x.y > lowestY).ToArray();
}
Can I do this with Linq?
Yes you can use an overload of Select that includes the index like so
var topPointIndexes = polygonCollider2D.points
.Select((p,i) => new{Point=p, Index=i})
.Where(x => x.Point.y > lowestY)
.Select(x => x.Index)
.ToArray();
Another option is to just create the set of indexes up front
var points = polygonCollider2D.points;
var topPointIndexes = Enumerable.Range(0, points.Count())
.Where(i => points[i].y > lowestY)
.ToArray();
The Select allows you to capture the index.
You can first select indexes and filter with the -1 'filter sentinel' value I used below,
topPointsIndexes = polygonCollider2D.points
.Select((x, index) => (x.y > lowestY) ? index : -1)
.Where(i => i >= 0)
.ToArray();
(or first expand the point and index together, and then filter, as juharr did in his answer)
indexesOfTopPoints = polygonCollider2D.points.Select((x, index) => index)
.Where(x => polygonCollider2D.points[x].y > lowestY).ToArray();
Try it
topPoints = polygonCollider2D.points.Select((x, i) => new {obj = x, i = i}).Where(x => x.y > lowestY).Select(x => x.i).ToArray();
I'm trying to query a nested linked list where result is the product of a child value multiplied by values in the parent list, and then the result is added.
This code works:
var X = (from B in Main.Globals.BookLL
from G in B.GreeksLL
where B.DealNo == 1 && B.Strategy == "Condor" &&
G.BookOrComp == "Book" && G.GreekType == "Delta"
select new
{
Total = G.Data[3] * B.BookPosn * B.FxRate
}).Sum(s=>s.Total);
...but I'd prefer to use lambda. This code below gives the compile error shown as a comment at the end of the line.
double Z = Globals.BookLL.Where(B => B.DealNo == 1 && B.Strategy == "Condor").
SelectMany(G => G.GreeksLL).
Where(G => G.BookOrComp == "Book" && G.GreekType == "Delta").
Select(G => new { Total = G.Data[3] * B.BookPosn*B.FxRate }). // Compile error "B does not exist in the current context"
Sum();
I don't know how to do this, please take a look and correct the query? Thanks.
Try:
double Z = Globals.BookLL.Where(B => B.DealNo == 1 && B.Strategy == "Condor").
SelectMany(par => par.GreeksLL, (parent, child) => new { G = child, B = parent }).
Where(both => both.G.BookOrComp == "Book" && both.G.GreekType == "Delta").
Select(both => new { Total = both.G.Data[3] * both.B.BookPosn*both.B.FxRate }).
Sum(x => x.Total);
My naming is a bit weird, but I hope you get the idea, basically you 'abandoned' B when you did SelectMany(), and this should be the way.
It is untested, so let me know if it works.
See MSDN for SelectMany() with results selector function.
Another approach is to filter the GreekLL inside the SelectMany using this overload, and then use Sum extension:
double z = Main.Globals.BookLL.Where(book => book.DealNo == 1 && book.Strategy == "Condor")
.SelectMany(book => book.GreeksLL.Where(greek => greek.BookOrComp == "Book" && greek.GreekType == "Delta")
,(book, greek) => new { Greek = greek, Book = book })
.Sum(greekAndBook => greekAndBook.Book.BookPosn * greekAndBook.Book.Fxrate * greekAndBook.Greek.Data[3]);
Mylist.GroupBy(x => new{x.X, x.Y}).Select(g => g.First()).ToList<XYZ>();
The above code works fine for me. I only want to compare the points based on the round(5) of the point component.
For example x.X = 16.838974347323224 should be only compared as x.X = 16.83897 because I experienced some inaccuracy after the round 5. Any suggestions?
Solution:
Mylist.GroupBy(x => new { X = Math.Round(x.X,5), Y = Math.Round(x.Y,5) })
.Select(g => g.First()).ToList();
Using Round can create a situation where two numbers, even though incredibly close to each other, can end up being considered distinct.
Take this example:
var Mylist = new []
{
new { X = 1.0000051, Y = 1.0 },
new { X = 1.0000049, Y = 1.0 },
new { X = 1.1, Y = 1.0 },
new { X = 1.0, Y = 1.005 },
};
The first two values are very close - in fact they differ in the 6th decimal place.
By what if we run this code:
var result =
Mylist
.GroupBy(x => new
{
X = Math.Round(x.X,5, MidpointRounding.AwayFromZero),
Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero)
})
.Select(g => g.First())
.ToList();
The result is:
The rounding has allowed these two values to be kept.
The correct approach is to filter by distance. If a subsequent value is within a threshold of the previous values it should be discarded.
Here's the code that does that:
var threshold = 0.000001;
Func<double, double, double, double, double> distance
= (x0, y0, x1, y1) =>
Math.Sqrt(Math.Pow(x1 - x0, 2.0) + Math.Pow(y1 - y0, 2.0));
var result = Mylist.Skip(1).Aggregate(Mylist.Take(1).ToList(), (xys, xy) =>
{
if (xys.All(xy2 => distance(xy.X, xy.Y, xy2.X, xy2.Y) >= threshold))
{
xys.Add(xy);
}
return xys;
});
Now if we run that on the Mylist data we get this:
This is a better ideal for removing duplicates.
To do so use Math.Round:
var result = Mylist.GroupBy(x => new { X = Math.Round(x.X,5, MidpointRounding.AwayFromZero), Y = Math.Round(x.Y,5, MidpointRounding.AwayFromZero) })
.Select(g => g.First()).ToList();
However if what you want is to remove duplicates then instead of GroupBy go for one of these:
Select rounded and then Distinct:
var result = Mylist.Select(item => new XYZ { X = Math.Round(item.X,5, MidpointRounding.AwayFromZero),
Y = Math.Round(item.Y,5, MidpointRounding.AwayFromZero)})
.Distinct().ToList();
Distinct and override Equals and GetHashCode - (equals will do the rounding) - wouldn't suggest
Distinct and implement a custom IEqualityComparer:
public class RoundedXyzComparer : IEqualityComparer<XYZ>
{
public int RoundingDigits { get; set; }
public RoundedXyzComparer(int roundingDigits)
{
RoundingDigits = roundingDigits;
}
public bool Equals(XYZ x, XYZ y)
{
return Math.Round(x.X, RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.X, RoundingDigits, MidpointRounding.AwayFromZero) &&
Math.Round(x.Y,RoundingDigits, MidpointRounding.AwayFromZero) == Math.Round(y.Y, RoundingDigits, MidpointRounding.AwayFromZero);
}
public int GetHashCode(XYZ obj)
{
return Math.Round(obj.X, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode() ^
Math.Round(obj.Y, RoundingDigits, MidpointRounding.AwayFromZero).GetHashCode();
}
}
//Use:
myList.Distinct(new RoundedXyzComparer(5));
I have a list of Points. List<Point> points = new List<Point>();
I want to get the biggest value of y coordinates inside the list, given the condition that it only scans the coordinates inside the list with a specific x coordinate.
For example:
I have points like this
(1,1)
(1,2)
(1,3)
(1,4)
(1,5)
(2,1)
(2,2)
(2,3)
(2,4)
(2,5)
I want to find the biggest value of y-axis or y-coordinate, given that it only search coordinates with 2 as value of x. so the output must be (2,5)
Use LINQ to get point with biggest Y coordinate:
Point maximumPoint = points.First(p => p.X == 2 &&
p.Y == points.Max(po => po.Y));
OR
Point maximumPoint = new Point(2, points.Where(p => p.X == 2).Max(p => p.Y));
With LINQ, you can do:
var result = points.Where(point => point.X == 2)
.Max(point => point.Y);
If you want the Point instead of just the Y-coordinate, it's simple enough to just new up a Point wih (2, result).
But in more complex cases, consider a MaxBy operator, such as the one that comes with morelinq.
With linq, this would by
var maxY = points.Where(p => p.X == 2).Select(p=> p.Y).Max();
This perhaps:
var maxY = points.Where(po => po.X == 2).Max(po => po.Y);
var q = points.Where(p =>
p.Y == maxY &&
p.X == 2).FirstOrDefault();