160

Given an array of Swift numeric values, how can I find the minimum and maximum values?

I've so far got a simple (but potentially expensive) way:

var myMax = sort(myArray,>)[0]

And how I was taught to do it at school:

var myMax = 0
for i in 0..myArray.count {
    if (myArray[i] > myMax){myMax = myArray[i]}
}

Is there a better way to get the minimum or maximum value from an integer Array in Swift? Ideally something that's one line such as Ruby's .min and .max.

2
  • 1
    Other answers explained how to get one. Now we can get both at once. Commented Dec 13, 2021 at 5:16
  • All this is now built-in to Swift with ".reduce". Use it. Commented May 28, 2024 at 19:35

18 Answers 18

356

Given:

let numbers = [1, 2, 3, 4, 5]

Swift 3:

numbers.min() // equals 1
numbers.max() // equals 5

Swift 2:

numbers.minElement() // equals 1
numbers.maxElement() // equals 5
Sign up to request clarification or add additional context in comments.

3 Comments

Works only on Comparable objects, so NSDecimalNumber won't work for example.
If your array is empty this returns 0: nums.min() ?? 0
and if you have an array of [Int?] just put a .flatMap{ $0 }.max() inbetween :)
112

To calculate an array's min and max values yourself, you can use reduce. This was a key solution prior to .min() and .max() appearing in Swift.


Use the almighty reduce:

let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, { max($0, $1) })

Similarly:

let numMin = nums.reduce(Int.max, { min($0, $1) })

reduce takes a first value that is the initial value for an internal accumulator variable, then applies the passed function (here, it's anonymous) to the accumulator and each element of the array successively, and stores the new value in the accumulator. The last accumulator value is then returned.

6 Comments

Perfect, just what I was looking for. There's a lot that's not in the iBook it seems!
Those are just general functional programming techniques, they're not specific to Swift.
@Jean-PhilippePellet you can actually simplify this to just: nums.reduce(Int.min, max) since max's prototype already matches the type that reduce is expecting
is there a reason why this does not work with arrays of doubles?
The min/max function signatures match the combine: parameter signature so you can just pass the function itself: let numMax = nums.reduce(Int.min, combine: max)
|
58

With Swift 5, Array, like other Sequence Protocol conforming objects (Dictionary, Set, etc), has two methods called max() and max(by:) that return the maximum element in the sequence or nil if the sequence is empty.


#1. Using Array's max() method

If the element type inside your sequence conforms to Comparable protocol (may it be String, Float, Character or one of your custom class or struct), you will be able to use max() that has the following declaration:

@warn_unqualified_access func max() -> Element?

Returns the maximum element in the sequence.

The following Playground codes show to use max():

let intMax = [12, 15, 6].max()
let stringMax = ["bike", "car", "boat"].max()

print(String(describing: intMax)) // prints: Optional(15)
print(String(describing: stringMax)) // prints: Optional("car")
class Route: Comparable, CustomStringConvertible {

    let distance: Int
    var description: String { return "Route with distance: \(distance)" }

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

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

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

}

let routes = [
    Route(distance: 20),
    Route(distance: 30),
    Route(distance: 10)
]

let maxRoute = routes.max()
print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)

#2. Using Array's max(by:) method

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

@warn_unqualified_access func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?

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

The following Playground codes show to use max(by:):

let dictionary = ["Boat" : 15, "Car" : 20, "Bike" : 40]

let keyMaxElement = dictionary.max(by: { (a, b) -> Bool in
    return a.key < b.key
})

let valueMaxElement = dictionary.max(by: { (a, b) -> Bool in
    return a.value < b.value
})

print(String(describing: keyMaxElement)) // prints: Optional(("Car", 20))
print(String(describing: valueMaxElement)) // prints: Optional(("Bike", 40))
class Route: CustomStringConvertible {

    let distance: Int
    var description: String { return "Route with distance: \(distance)" }

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

}

let routes = [
    Route(distance: 20),
    Route(distance: 30),
    Route(distance: 10)
]

let maxRoute = routes.max(by: { (a, b) -> Bool in
    return a.distance < b.distance
})

print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)

1 Comment

In Swift 3 "maxElement" has been renamed to "max"
17

The other answers are all correct, but don't forget you could also use collection operators, as follows:

var list = [1, 2, 3, 4]
var max: Int = (list as AnyObject).valueForKeyPath("@max.self") as Int

you can also find the average in the same way:

var avg: Double = (list as AnyObject).valueForKeyPath("@avg.self") as Double

This syntax might be less clear than some of the other solutions, but it's interesting to see that -valueForKeyPath: can still be used :)

Comments

12

You can use with reduce:

let randomNumbers = [4, 7, 1, 9, 6, 5, 6, 9]
let maxNumber = randomNumbers.reduce(randomNumbers[0]) { $0 > $1 ? $0 : $1 } //result is 9

Comments

6

Swift 3.0

You can try this code programmatically.

func getSmallAndGreatestNumber() -> Void {

    let numbers = [145, 206, 116, 809, 540, 176]
    var i = 0
    var largest = numbers[0]
    var small = numbers[0]
    while i < numbers.count{

        if (numbers[i] > largest) {
            largest = numbers[i]
        }
        if (numbers[i] < small) {
            small = numbers[i]
        }
        i = i + 1
    }
    print("Maximum Number ====================\(largest)")// 809
    print("Minimum Number ====================\(small)")// 116
}

Comments

5

With Swift 1.2 (and maybe earlier) you now need to use:

let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, combine: { max($0, $1) })

For working with Double values I used something like this:

let nums = [1.3, 6.2, 3.6, 9.7, 4.9, 6.3];
let numMax = nums.reduce(-Double.infinity, combine: { max($0, $1) })

1 Comment

You could also just do this let numMax = nums.reduce(-Double.infinity, combine: max), the max function signature matches the combine: parameter signature.
4

In Swift 2.0, the minElement and maxElement become methods of SequenceType protocol, you should call them like:

let a = [1, 2, 3]
print(a.maxElement()) //3
print(a.minElement()) //1

Using maxElement as a function like maxElement(a) is unavailable now.

The syntax of Swift is in flux, so I can just confirm this in Xcode version7 beta6.

It may be modified in the future, so I suggest that you'd better check the doc before you use these methods.

Comments

4

Here's a performance test for the solutions posted here. https://github.com/tedgonzalez/MaxElementInCollectionPerformance

This is the fastest for Swift 5

array.max()

Comments

3
var numbers = [1, 2, 7, 5];    
var val = sort(numbers){$0 > $1}[0];

2 Comments

To me this looks much like var myMax = sort(myArray,>)[0]
Sort has too much overhead.
2

This is noawadays completely built in to Swift: .min() .max()

let numbers = [3, 9, 2] 

let min = numbers.min()
let max = numbers.max()

As the Op asked,

Ideally something that's one line such as Ruby's .min and .max

you have to add "()" and then it's the same as Ruby :)

Example

when graphing ...

guard let min = x.min(), let max = x.max(), max > min else { return ... }
let r = max - min
let normalized = x.compactMap{ ($0 - min) / r }

Comments

2

Apple's Swift Algorithms introduced 2021 contains Minima and/or Maxima which is likely highly optimized.

Example from the documentation:

let numbers = [7, 1, 6, 2, 8, 3, 9]
if let (smallest, largest) = numbers.minAndMax(by: <) {
    // Work with 1 and 9....
}

The total complexity is O(k log k + nk), which will result in a runtime close to O(n) if k is a small amount. If k is a large amount (more than 10% of the collection), we fall back to sorting the entire array. Realistically, this means the worst case is actually O(n log n).

Comments

1

If both minimum and maximum values are desired, an efficient approach could be to use a single reduce operation with a tuple:

let values = [11, 2, 7, 5, 21]

let (minimum, maximum) = values.reduce((Int.max, Int.min)) {
    (min($0.0, $1), max($0.1, $1))
}

print(minimum, maximum) // 2, 21

Comments

0

Updated for Swift 3/4:

Use below simple lines of code to find the max from array;

var num = [11, 2, 7, 5, 21]
var result = num.sorted(){
    $0 > $1
}
print("max from result: \(result[0])") // 21

Comments

0

Just curious why you think the way it was taught in school is potentially expensive? You're running a for loop which has the time complexity of O(N). That's actually better than most sorting algorithms and equivalent to the performance of higher-order methods like reduce.

So I would think that in terms of performance, a for loop is as good as it gets. I don't think you'll find anything better than O(N) for finding max.

Of course, just use the .max() method provided by apple is the way to go.

1 Comment

I'm referring to the one using sort when talking about the expensive one.
0
let array: [Int] = [2, -22, -1, -5600, 333, -167]
var min = 0
var max = array[0]

for i in array {
    // finding the min
    if min > i {
        min = i
    }
    // finding the max
    if max < i {
        max = i
    }
}

print("Minimum: \(min)\nMaximum: \(max)")

Comments

0

You can write extension like

extension Array {
    func maxValue<T: Comparable>(_ keyPath: KeyPath<Element, T>, default defaultValue: T) -> T {
        let element = self.max(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] })
        if let element {
            return element[keyPath: keyPath]
        }
        
        return defaultValue
    }
}

so that you can use it more conveniently

let students: [Student] = ...
let maxScore = students.maxValue(\.study.score, default: 0)

Comments

-2

You can also sort your array and then use array.first or array.last

3 Comments

This is computationally slower. You can find the max in linear time.
I'm a very new @CharlieEgan, can you explain linear time or point me to a tutorial. Thanks a lot
do some reading around 'time complexity' (en.wikipedia.org/wiki/Time_complexity). This is also worth a read: bigocheatsheet.com. Some good worked examples here: khanacademy.org/computing/computer-science/algorithms

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.