Swift
Array
MinMax
Programming
Tutorial

Find min / max value in Swift Array

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Finding minimum and maximum values in a Swift array is a frequent task in app logic, analytics, and UI summaries. Swift provides concise built-in APIs that are efficient and easy to read. The best approach depends on whether you need both values, custom comparison rules, or safe handling for empty arrays.

Built-in min and max

For arrays of comparable values, call min() and max() directly. Both return optionals because arrays can be empty.

swift
1let scores = [12, 5, 28, 19, 7]
2
3if let minValue = scores.min(), let maxValue = scores.max() {
4    print("min:", minValue)
5    print("max:", maxValue)
6}

This is usually the cleanest option for standard numeric arrays.

Get Both Values in One Pass

Calling min and max separately can scan twice. Swift also offers minAndMax style logic through reduce when you want full control and a single pass.

swift
1func minMax(_ values: [Int]) -> (min: Int, max: Int)? {
2    guard let first = values.first else { return nil }
3
4    return values.dropFirst().reduce((min: first, max: first)) { acc, value in
5        (min: Swift.min(acc.min, value), max: Swift.max(acc.max, value))
6    }
7}
8
9if let result = minMax([9, 2, 17, 3, 11]) {
10    print(result.min, result.max)
11}

This approach is useful in performance-sensitive paths where data volume is high.

Custom Comparison with Objects

If array elements are custom types, use min(by:) and max(by:) with explicit comparison closures.

swift
1struct Product {
2    let name: String
3    let price: Double
4}
5
6let items = [
7    Product(name: "A", price: 49.0),
8    Product(name: "B", price: 29.5),
9    Product(name: "C", price: 95.0)
10]
11
12if let cheapest = items.min(by: { $0.price < $1.price }),
13   let mostExpensive = items.max(by: { $0.price < $1.price }) {
14    print(cheapest.name, mostExpensive.name)
15}

Keep the comparison rule centralized so business semantics remain consistent.

Handling Empty Arrays Safely

Never force unwrap min or max results unless array emptiness is impossible by design. Guard statements keep control flow explicit.

swift
1func rangeLabel(for values: [Double]) -> String {
2    guard let lo = values.min(), let hi = values.max() else {
3        return "No data"
4    }
5    return "Range: \(lo) to \(hi)"
6}
7
8print(rangeLabel(for: []))
9print(rangeLabel(for: [1.2, 4.8, 3.1]))

This prevents crashes in edge cases such as filtered datasets.

Generic Utility Pattern

You can define a reusable generic helper for any comparable type.

swift
1func minMaxGeneric<T: Comparable>(_ values: [T]) -> (T, T)? {
2    guard let first = values.first else { return nil }
3    var lo = first
4    var hi = first
5
6    for v in values.dropFirst() {
7        if v < lo { lo = v }
8        if v > hi { hi = v }
9    }
10    return (lo, hi)
11}
12
13if let (lo, hi) = minMaxGeneric(["pear", "apple", "orange"]) {
14    print(lo, hi)
15}

Generic helpers are especially useful in shared utility modules.

Performance Notes for Large Data

For very large arrays, repeated min and max queries can dominate time if recomputed frequently. If values change in batches, compute range once and cache it near the data source.

swift
1struct MetricWindow {
2    private(set) var values: [Double] = []
3    private(set) var cachedRange: (Double, Double)?
4
5    mutating func replace(with newValues: [Double]) {
6        values = newValues
7        cachedRange = minMaxGeneric(newValues)
8    }
9}
10
11var window = MetricWindow()
12window.replace(with: [3.0, 8.4, 2.1, 9.6])
13print(window.cachedRange as Any)

Caching is useful in chart-heavy screens that render frequently.

Working with Optionals and Filtering

Real datasets often contain optional values. Compact optional arrays first, then compute range safely.

swift
1let raw: [Int?] = [3, nil, 11, 2, nil, 8]
2let clean = raw.compactMap { $0 }
3
4if let lo = clean.min(), let hi = clean.max() {
5    print("range", lo, hi)
6}

This keeps transformation and range logic explicit.

Common Pitfalls

  • Force unwrapping min() or max() on potentially empty arrays.
  • Calling separate scans in hot loops without measuring impact.
  • Duplicating comparison logic for custom objects in many files.
  • Using inconsistent comparison rules across features.
  • Ignoring optional handling when array content comes from filters.

Summary

  • Use built-in min() and max() for simple comparable arrays.
  • Use one-pass logic when performance or custom behavior matters.
  • Use min(by:) and max(by:) for custom types.
  • Handle empty arrays with safe optional patterns.
  • Centralize comparison logic in reusable utilities.

Course illustration
Course illustration

All Rights Reserved.