问题
We have an array of CGPoints:
let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)]
What I'm trying to do is get the CGPoint with the highest x value and the one with the highest y value in the array. I will be using these points to create a CGRect:
extension CGRect {
init(p1: CGPoint, p2: CGPoint) {
self.init(x: min(p1.x, p2.x),
y: min(p1.y, p2.y),
width: abs(p1.x - p2.x),
height: abs(p1.y - p2.y))
}
}
I know there a way to get max and min values in an array by doing something like this:
points.min()
points.max()
but these don't seem to work since its an array of CGPoints. Is it possible to get these values from the array?
回答1:
You can use max(by:)
.
let minXPoint = points.min(by: {$0.x < $1.x}) //(1152.0, 1043.0)
let maxYPoint = points.max(by: {$0.y < $1.y}) //(1241.0, 1111.0)
回答2:
You can't do that with just a single call to min()
/max()
off of the highest and lowest of the y and x coordinates
The point with the minimal x value could be different from the point with the minimal x value. So any method that returns only one of the points (as min()
/max()
do) isn't sufficient.
You either need two separate calls to each, like:
let minX = points.min(by: { $0.x < $1.x })!.x
let minY = points.min(by: { $0.y < $1.y })!.y
let maxX = points.min(by: { $0.x > $1.x })!.x
let maxY = points.min(by: { $0.y > $1.y })!.y
or you could try doing it all in one swoop with reduce:
let initialAccumulator = (
minX: CGFloat.max, minY: CGFloat.max, maxX: CGFloat.min, maxY: CGFloat.min)
let (minX, minY, maxX, maxY) = points.reduce(initialAccumulator) { accumulator, point in
return (
minX: min(accumulator.minX, point.x),
minY: min(accumulator.minY, point.y),
maxX: max(accumulator.maxX, point.x),
maxY: max(accumulator.maxY, point.y)
)
}
I would probably do this like so:
extension CGRect {
static func minimalRect(containing points: [CGPoint]) -> CGRect? {
if points.isEmpty { return nil }
let minX = points.min(by: { $0.x < $1.x })!.x
let minY = points.min(by: { $0.y < $1.y })!.y
let maxX = points.min(by: { $0.x > $1.x })!.x
let maxY = points.min(by: { $0.y > $1.y })!.y
return CGRect(
x: minX,
y: minY,
width: (minX - maxX).magnitude,
height: (minY - maxY).magnitude
)
}
}
let points = [(1234.0, 1053.0), (1241.0, 1111.0), (1152.0, 1043.0)].map(CGPoint.init)
let r = CGRect.minimalRect(containing: points)
print(r ?? .zero) // => (1152.0, 1043.0, 89.0, 68.0)
回答3:
You can map values to find the min and max in x and y coordinates like below. If you're not sure if points
array contains any data, use guard statement to avoid force unwrapping:
let xArray = points.map(\.x)
let yArray = points.map(\.y)
guard let minX = xArray.min(),
let maxX = xArray.max(),
let minY = yArray.min(),
let maxY = yArray.max() else { return }
And from there:
let minPoint = CGPoint(x: minX, y: minY)
let maxPoint = CGPoint(x: minY, y: minY)
then you can modify your extension function because you already know which values are min and max:
extension CGRect {
init(minPoint: CGPoint, maxPoint: CGPoint) {
self.init(x: minPoint.x,
y: minPoint.y,
width: maxPoint.x - minPoint.x,
height: maxPoint.y - minPoint.y)
}
}
As Leo Dabus suggested in the comment below, you can do it all in one go inside failable initializer extension:
extension CGRect {
init?(points: [CGPoint]) {
let xArray = points.map(\.x)
let yArray = points.map(\.y)
if let minX = xArray.min(),
let maxX = xArray.max(),
let minY = yArray.min(),
let maxY = yArray.max() {
self.init(x: minX,
y: minY,
width: maxX - minX,
height: maxY - minY)
} else {
return nil
}
}
}
来源:https://stackoverflow.com/questions/61843897/get-the-max-and-min-values-from-an-array-of-cgpoints