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

Charlie Egan
fuente
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
Rudolf Adamkovič
fuente
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.

Jean-Philippe Pellet
fuente
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)
Imanou Petit
fuente
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 :)

Sam
fuente
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
Khuong
fuente
4
var numbers = [1, 2, 7, 5];    
var val = sort(numbers){$0 > $1}[0];
androabhay
fuente
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) })
Conquista de Allen
fuente
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.

Shi XiuFeng
fuente
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
}
Sankalap Yaduraj Singh
fuente
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
Kiran jadhav
fuente
-1

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

Saad Ghadir
fuente
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