I would like to use a single LINQ aggregate over a collection of (latitude, longitude) pairs and produce two (latitude, longitude) pairs:
public Location {
public double Latitude;
public double Longitude;
}
List<Location> border = ...;
I can easily obtain a minimum (latitude, longitude) pair by:
var minBorder = border.Aggregate( new Location()
{ Latitude = double.MaxValue, Longitude = double.MaxValue },
(current, next) =>
new Location()
{
Latitude = (next.Latitude < current.Latitude) ? next.Latitude : current.Latitude,
Longitude = (next.Longitude < current.Longitude) ? next.Longitude : current.Longitude
}) ;
If possible, I would like to use a single aggregate to return two Locations; a minimum (latitude, longitude) pair and a maximum (latitude, longitude) pair instead of one.
If I declare a class for results:
public class BorderBounds {
public double MinLatitude;
public double MinLongitude;
public double MaxLatitude;
public double MaxLongitude;
}
and modify the aggregate:
var borderBounds = border.Aggregate( new Location()
{ Latitude = double.MaxValue, Longitude = double.MaxValue },
(current, next) =>
new BorderBounds()
{
...
}) ;
the (current, next)
parameters are assumed to be of type BorderBounds
instead of Location
.
Is there a way to construct such an aggregate? Would it be best to simply convert this to a foreach
?
You can do it. I would suggest making the bounds mutable, or making a mutable bounds builder that can create a bounds object afterwards, just to save on the unnecessary memory allocation:
locations.Aggregate(new Bounds(), (bounds, location) =>
{
if (bounds.MinLat > location.Latitude) bounds.MinLat = location.Latitude;
if (bounds.MaxLat < location.Latitude) bounds.MaxLat = location.Latitude;
if (bounds.MinLon > location.Longitude) bounds.MinLon = location.Longitude;
if (bounds.MaxLon < location.Longitude) bounds.MaxLon = location.Longitude;
return bounds;
});
And the classes
internal class Location
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
internal class Bounds
{
public Bounds()
{
MinLat = double.MaxValue;
MaxLat = double.MinValue;
MinLon = double.MaxValue;
MaxLon = double.MinValue;
}
public double MinLat { get; set; }
public double MaxLat { get; set; }
public double MinLon { get; set; }
public double MaxLon { get; set; }
}
border
.Aggregate(new BorderBounds()
{ MinLatitude = double.MaxValue,
MinLongitude = double.MaxValue,
MaxLongitude = double.MinValue,
MaxLatitude = double.MinValue },
(current, next) => new BorderBounds {
MinLatitude = next.Latitude < current.MinLatitude ? next.Latitude : current.MinLatitude,
MinLongitude = next.Longitude < current.MinLongitude ? next.Longitude : current.MinLongitude,
MaxLatitude = next.Latitude > current.MaxLatitude ? next.Latitude : current.MaxLatitude,
MaxLongitude = next.Longitude > current.MaxLongitude ? next.Longitude : current.MaxLongitude
}
);
The return type of an Aggregate function is the same as the seed passed in, and not of the collection itself.
I'm assuming you're constructing something like a bounding box here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With