1

Drawing from Correct way to find max in an Array in Swift, I am attempting to find the leftmost position in Swift array using reduce. I would have thought that this would work:

var a = [CGPoint(x:1,y:1),CGPoint(x:2,y:2),CGPoint(x:0,y:0)]
var leftMost = a.reduce(CGPoint(x:CGFloat.max,y:CGFloat.max)) {min($0.x,$1.x)}

However, I get this error:

`Type 'CGPoint' does not conform to protocol 'Comparable'

Of course, I'm not comparing a CGPoint, I'm comparing the point .x, whic should be a CGFloat.

Ideas?

Community
  • 1
  • 1
vy32
  • 28,461
  • 37
  • 122
  • 246

2 Answers2

8

The array holds CGPoint, whereas in the reduce closure you're trying to return a CGFloat - you have to convert it to a CGPoint instead:

var leftMost = a.reduce(CGPoint(x:CGFloat.greatestFiniteMagnitude,y:CGFloat.greatestFiniteMagnitude)) {
    CGPoint(x: min($0.x,$1.x), y: $0.y)
}.x

I know, the error message doesn't help much :)

Another way to achieve the same result is by transforming the points into x coordinates first, and then reduce:

var leftMost = a.map { $0.x }.reduce(CGFloat.greatestFiniteMagnitude) { min($0,$1) }

maybe easier to read, although probably more expensive in terms of performance. But as @MartinR suggests, it can also be simplified as:

var leftMost = a.reduce(CGFloat.greatestFiniteMagnitude) { min($0, $1.x) }
Politta
  • 400
  • 4
  • 10
Antonio
  • 71,651
  • 11
  • 148
  • 165
  • 1
    The second solution can also be written as `var leftMost = a.reduce(CGFloat.max) { min($0, $1.x) }` – easy to read *and* performant :) – Martin R Feb 16 '15 at 22:10
  • @MartinR: today your task is to find simplified versions of my solutions... isn't it? ;-) Thanks again – Antonio Feb 16 '15 at 22:17
  • Thanks. In fact `var leftMost = a.reduce(CGFloat.max) { min($0, $1.x) }` is what I was looking for. Both you and @MartinR should share! – vy32 Feb 16 '15 at 23:37
  • 1
    @Antonio: I would not bet on the performance for either of your solutions. I know that such kind of loop is easily recognized and optimized by a compiler ending up with almost similar machine code. Though I can't speak for Swift. If performance counts one should anyway make benchmarks. But in general readability rules. – qwerty_so Feb 16 '15 at 23:39
  • This returns min value from array of CGPoint, I also need max X from array of CGPoint, try to get it by using max instead of min, but it didn't work for me. Can you guys share code for find max X from array of CGPoint? Thanks in advance. :) – Anjali jariwala May 15 '19 at 06:46
  • @Anjalijariwala have you replaced `CGFloat.greatestFiniteMagnitude` with either `CGFloat. leastNonzeroMagnitude ` or `CGFloat. leastNormalMagnitude `? – Antonio May 15 '19 at 10:20
2

Swift 5 implementation:

Usage:

let leftMost = self.findPoint(points: points, position: .leftMost)

Function and enum:

  enum Position {
    case topMost
    case bottomMost
    case leftMost
    case rightMost
  }
  func findPoint(points: [CGPoint], position: Position) -> CGPoint? {
    var result: CGPoint?
    switch (position) {
    case .bottomMost:
      result = points.max { a, b in a.y < b.y }
    case .topMost:
      result = points.max { a, b in a.y > b.y }
    case .leftMost:
      result = points.max { a, b in a.x > b.x }
    case .rightMost:
      result = points.max { a, b in a.x < b.x }
    }
    return result
  }
elliott-io
  • 1,394
  • 6
  • 9