1

I have a bunch of Objects stored in an Array.

They all have the property:

distanceInSeconds: Int

I was wondering if there's a way to find the max of this property between all objects in the array using filter or another array method?

For instance:

var distances: [Distance] = []
var maxDistance = distances.filter(find max)

4 Answers 4

3

This would be the Swifty way (by implementing Comparable):

class Route : Comparable {
    let distance: Int

    init(distance: Int) {
        self.distance = distance
    }
}

func ==(lhs: Route, rhs: Route) -> Bool {
    return lhs.distance == rhs.distance
}

func <(lhs: Route, rhs: Route) -> Bool {
    return lhs.distance < rhs.distance
}

let routes = [
    Route(distance: 4),
    Route(distance: 8),
    Route(distance: 2),
    Route(distance: 7)
]

print(routes.maxElement()?.distance)

output:

"8"

This works with Swift 2. If you're using Swift 1.2, maxElement(routes) should work

Sign up to request clarification or add additional context in comments.

5 Comments

What am I doing wrong? When I implement this class it says it's not conforming to the protocol?
@KristoferDoman Maybe you implemented it within the class itself, but because it's an operator requirement, it has to be global. Does my code not work for you? It's tested with Swift 2
It doesn't seem to be, and no, I implemented it outside the class. Do both of them need to be implemented in order for it to conform to protocol?
I'm also using 1.2, I'm unsure if there's any major differences in this regard.
@KristoferDoman I really don't know what your problem is, I just tried it with Swift 1.2 in a Playground and it was working just fine (exchanging the last line with print(maxElement(routes).distance))
3

In Swift 2.0 minElement and maxElement now return optionals in case of empty sequences, and also now have versions that take isOrderedBefore closures.

    let maxDistance = distances.maxElement { (a, b) -> Bool in
        a.distanceInSeconds < b.distanceInSeconds
    }

Comments

1

#1. The element type inside your sequence conforms to Comparable protocol

With Swift 4, if the element type inside your sequence conforms to Comparable protocol, you will be able to use the max() method that has the following declaration:

func max() -> Self.Element?

Returns the maximum element in the sequence.

Usage:

class Distance: Comparable, CustomStringConvertible {

    let distanceInSeconds: Int
    var description: String { return "Distance in Int: \(distanceInSeconds)" }

    init(distanceInSeconds: Int) {
        self.distanceInSeconds = distanceInSeconds
    }

    static func ==(lhs: Distance, rhs: Distance) -> Bool {
        return lhs.distanceInSeconds == rhs.distanceInSeconds
    }

    static func <(lhs: Distance, rhs: Distance) -> Bool {
        return lhs.distanceInSeconds < rhs.distanceInSeconds
    }

}

let distances = [
    Distance(distanceInSeconds: 20),
    Distance(distanceInSeconds: 30),
    Distance(distanceInSeconds: 10)
]

let maxDistance = distances.max()
print(String(describing: maxDistance)) // prints: Optional(Distance in Int: 30)

#2. The element type inside your sequence does not conform to Comparable protocol

With Swift 4, if the element type inside your sequence does not conform to Comparable protocol, you will have to use the max(by:) method that has the following declaration:

func max(by areInIncreasingOrder: ((offset: Int, element: Base.Element), (offset: Int, element: Base.Element)) throws -> Bool) rethrows -> (offset: Int, element: Base.Element)?

Returns the maximum element in the sequence, using the given predicate as the comparison between elements.

Usage:

class Distance: CustomStringConvertible {

    let distanceInSeconds: Int
    var description: String { return "Distance in Int: \(distanceInSeconds)" }

    init(distanceInSeconds: Int) {
        self.distanceInSeconds = distanceInSeconds
    }

}

let distances = [
    Distance(distanceInSeconds: 20),
    Distance(distanceInSeconds: 30),
    Distance(distanceInSeconds: 10)
]

let maxDistance = distances.max (by: { (a, b) -> Bool in
    return a.distanceInSeconds < b.distanceInSeconds
})

print(String(describing: maxDistance)) // prints: Optional(Distance in Int: 30)

Comments

0

I think you want reduce:

Setup:

struct Distance {
    var distanceInSeconds: Int
}

var distances: [Distance] = []
for _ in 1...10 {
    distances += [Distance(distanceInSeconds: Int(arc4random_uniform(100)))]
}

Implementation:

let max = distances.reduce(distances.first) {
    if let left = $0 where left.distanceInSeconds > $1.distanceInSeconds {
        return $0
    } else {
        return $1
    }
}

9 Comments

Pretty close. Should be: ... if $0.distanceInSeconds < $1.distanceInSeconds { ... – also, the reduce(_) argument should be a distance object with 0 distanceInSeconds
$0 says it's of type Int, and $1 says it's of type Distance, so there's a flaw there I think.
Where he has put $0.distanceInSeconds it should just be $0
Thanks all, was typing from memory! This one should work!
Sorry; I realised later that the reduce argument should not be 0, but a distance object with 0 distanceInSeconds
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.