Forma correcta de encontrar el máximo en una matriz en Swift


121

Hasta ahora tengo una forma simple (pero potencialmente costosa):

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

Y cómo me enseñaron a hacerlo en la escuela:

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

¿Existe una mejor manera de obtener el valor máximo de una matriz de enteros en Swift? Idealmente, algo que sea de una línea como Ruby.max


Escribe una extensión.
— gnasher729

Sí, una línea: maxElement(myArray). Vea cuál es actualmente la segunda respuesta (la de Rudolf Adamkovic) a continuación.
— leekaiinthesky

Yo cambio que aceptó la respuesta a esto
— mattgabor

@mattymcgee He actualizado la respuesta aceptada.
— Charlie Egan

Respuestas:


299

Dado:

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

Swift 3:

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

Rápido 2:

numbers.minElement() // equals 1
numbers.maxElement() // equals 5

2
Funciona solo en Comparableobjetos, por NSDecimalNumberlo que no funcionará, por ejemplo.
— Michał Hernas

2
¿Soy solo yo o estas funciones no existen en Swift 2?
— Liron Yahdav

@LironYahdav Ahora son métodos. Fijo. ¡Gracias!
— Rudolf Adamkovič

2
Tenga en cuenta que en Swift 3 se les ha cambiado el nombre a simplemente min()y max().
— jemmons

1
@Jezzamon No. En Swift 3, los métodos minElementy maxElementfueron renombrados a miny max. ver: github.com/apple/swift-evolution/blob/master/proposals/… Entiendo su confusión, porque las funciones gratuitas miny maxtambién existen. Ver, por ejemplo, gist.github.com/lorentey/d679064cb29df4558534d619319a1d9e
— jemmons

95

Actualización: esta probablemente debería ser la respuesta aceptada desde que maxElementapareció en Swift.


Usa el todopoderoso reduce:

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

Similar:

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

reducetoma un primer valor que es el valor inicial para una variable acumuladora interna, luego aplica la función pasada (aquí, es anónima) al acumulador y a cada elemento de la matriz sucesivamente, y almacena el nuevo valor en el acumulador. A continuación, se devuelve el último valor del acumulador.


1
Perfecto, justo lo que buscaba. ¡Parece que hay muchas cosas que no están en el iBook!
— Charlie Egan

2
Esas son solo técnicas de programación funcional generales, no son específicas de Swift.
— Jean-Philippe Pellet

10
@ Jean-PhilippePellet, en realidad puede simplificar esto a solo: nums.reduce(Int.min, max)dado que maxel prototipo de 'ya coincide con el tipo que reducese espera
— drewag

¿Hay alguna razón por la que esto no funcione con matrices de dobles?
— Nicholas

3
Las firmas de función mín. / Máx. Coinciden con la firma del parámetro combine: para que pueda pasar la función en sí:let numMax = nums.reduce(Int.min, combine: max)
— Leslie Godwin

38

Con Swift 5, Arrayal igual que otros Sequenceobjetos de protocolo conforme ( Dictionary, Set, etc.), tiene dos métodos llamados max()y max(by:)que retornan el máximo elemento en la secuencia o nilsi la secuencia está vacía.


# 1. Usando Arrayel max()método de

Si el tipo de elemento dentro de la secuencia se ajusta a Comparableprotocolos (puede ser String, Float, Charactero uno de su clase personalizada o estructura), usted será capaz de usar max()que tiene la siguiente declaración :

@warn_unqualified_access func max() -> Element?

Devuelve el elemento máximo en la secuencia.

Se muestran los siguientes códigos de Playground para usar 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. Usando Arrayel max(by:)método de

Si el tipo de elemento dentro de su secuencia no se ajusta al Comparableprotocolo, deberá usar max(by:)que tenga la siguiente declaración :

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

Devuelve el elemento máximo en la secuencia, utilizando el predicado dado como comparación entre elementos.

Se muestran los siguientes códigos de Playground para usar 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)

En Swift 3, "maxElement" ha cambiado de nombre a "max"
— Nicolai Henriksen

16

Las otras respuestas son todas correctas, pero no olvide que también puede usar operadores de recolección, de la siguiente manera:

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

también puedes encontrar el promedio de la misma manera:

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

Esta sintaxis puede ser menos clara que algunas de las otras soluciones, pero es interesante ver que -valueForKeyPath:aún se puede usar :)


11

Puede usar con reduce:

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

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

2
Para mí esto se parece mucho avar myMax = sort(myArray,>)[0]
— Charlie Egan

3
Sort tiene demasiados gastos generales.
— vy32

4

Con Swift 1.2 (y tal vez antes) ahora necesita usar:

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

Para trabajar con valores dobles usé algo como esto:

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
También puede hacer esto let numMax = nums.reduce(-Double.infinity, combine: max), la firma de la función máxima coincide con la firma del parámetro combine :.
— Leslie Godwin

3

En Swift 2.0, los métodos de protocolo minElementy se maxElementconvierten SequenceType, debería llamarlos como:

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

El uso maxElementcomo función como nomaxElement(a) está disponible ahora.

La sintaxis de Swift está cambiando, así que puedo confirmar esto en Xcode versión 7 beta6 .

Es posible que se modifique en el futuro, por lo que le sugiero que consulte el documento antes de usar estos métodos.


3

Swift 3.0

Puede probar este código mediante programación.

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
}


0

Actualizado para Swift 3/4:

Utilice las siguientes líneas de código simples para encontrar el máximo de la matriz;

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

-1

También puede ordenar su matriz y luego usar array.firstoarray.last


5
Esto es computacionalmente más lento. Puede encontrar el máximo en tiempo lineal.
— Charlie Egan

Soy un @CharlieEgan muy nuevo, ¿puedes explicarme el tiempo lineal o señalarme un tutorial? Muchas gracias
— Saad Ghadir

lea un poco sobre la 'complejidad del tiempo' ( en.wikipedia.org/wiki/Time_complexity ). También vale la pena leerlo: bigocheatsheet.com . Algunos ejemplos bien trabajados aquí: khanacademy.org/computing/computer-science/algorithms
— Charlie Egan
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.