Modo corretto per trovare il massimo in un array in Swift


121

Finora ho un modo semplice (ma potenzialmente costoso):

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

E come mi è stato insegnato a farlo a scuola:

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

C'è un modo migliore per ottenere il valore massimo da un intero array in Swift? Idealmente qualcosa che è una linea come Ruby.max


Scrivi un'estensione.
gnasher729

Sì, una sola riga: maxElement(myArray). Guarda qual è attualmente la seconda risposta (quella di Rudolf Adamkovic) di seguito.
leekaiinthesky

Cambia la risposta accettata a questa
mattgabor

@mattymcgee Ho aggiornato la risposta accettata.
Charlie Egan

Risposte:


299

Dato:

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

2
Funziona solo sugli Comparableoggetti, quindi NSDecimalNumbernon funzionerà per esempio.
Michał Hernas

2
Sono solo io o queste funzioni non esistono in Swift 2?
Liron Yahdav

@ LironYahdav Ora sono metodi. Fisso. Grazie!
Rudolf Adamkovič

2
Nota in Swift 3 questi sono stati rinominati semplicemente min()e max().
jemmons

1
@ Jezzamon No. In Swift 3 i metodi minElemente maxElementsono stati rinominati in mine max. vedi: github.com/apple/swift-evolution/blob/master/proposals/… Capisco la tua confusione, perché le funzioni libere mine maxanche esistono ancora. Vedi, ad esempio, gist.github.com/lorentey/d679064cb29df4558534d619319a1d9e
jemmons

95

Aggiornamento: questa dovrebbe probabilmente essere la risposta accettata poiché è maxElementapparsa in Swift.


Usa l'Onnipotente reduce:

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

Allo stesso modo:

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

reduceprende un primo valore che è il valore iniziale per una variabile accumulatore interna, quindi applica la funzione passata (qui, è anonima) all'accumulatore e ogni elemento dell'array successivamente e memorizza il nuovo valore nell'accumulatore. Viene quindi restituito l'ultimo valore dell'accumulatore.


1
Perfetto, proprio quello che stavo cercando. Sembra che ci siano molte cose che non sono nell'iBook!
Charlie Egan

2
Queste sono solo tecniche di programmazione funzionale generali, non sono specifiche di Swift.
Jean-Philippe Pellet

10
@ Jean-PhilippePellet puoi effettivamente semplificarlo semplicemente: nums.reduce(Int.min, max)poiché maxil prototipo di corrisponde già al tipo che reducesi aspetta
drewag

c'è un motivo per cui questo non funziona con array di doppi?
Nicholas

3
Le firme della funzione min / max corrispondono alla firma del parametro combina: quindi puoi semplicemente passare la funzione stessa:let numMax = nums.reduce(Int.min, combine: max)
Leslie Godwin

38

Con Swift 5, Arraycome altri Sequenceoggetti conforme Protocol ( Dictionary, Set, ecc), ha due metodi chiamati max()e max(by:)che riportare l'elemento massimo nella sequenza o nilse la sequenza è vuota.


# 1. Utilizzando Arrayil max()metodo di

Se il tipo di elemento all'interno del vostro conforme sequenza di Comparableprotocollo (può essere String, Float, Charactero uno della classe personalizzata o struct), si sarà in grado di utilizzare max()che ha la seguente dichiarazione :

@warn_unqualified_access func max() -> Element?

Restituisce l'elemento massimo nella sequenza.

I seguenti codici Playground mostrano di utilizzare 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. Utilizzando Arrayil max(by:)metodo di

Se il tipo di elemento all'interno della sequenza non è conforme al Comparableprotocollo, dovrai utilizzarlo max(by:)con la seguente dichiarazione :

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

Restituisce l'elemento massimo nella sequenza, utilizzando il predicato dato come confronto tra gli elementi.

I seguenti codici Playground mostrano di utilizzare 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)

In Swift 3 "maxElement" è stato rinominato "max"
Nicolai Henriksen

16

Le altre risposte sono tutte corrette, ma non dimenticare che potresti anche utilizzare operatori di raccolta, come segue:

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

puoi anche trovare la media allo stesso modo:

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

Questa sintassi potrebbe essere meno chiara di alcune delle altre soluzioni, ma è interessante vedere che -valueForKeyPath:può ancora essere utilizzata :)


11

Puoi usare 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
A me sembra molto simile avar myMax = sort(myArray,>)[0]
Charlie Egan il

3
L'ordinamento ha un sovraccarico eccessivo.
vy32

4

Con Swift 1.2 (e forse anche prima) ora devi usare:

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

Per lavorare con i valori Double ho usato qualcosa del genere:

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
Potresti anche farlo let numMax = nums.reduce(-Double.infinity, combine: max), la firma della funzione max corrisponde alla firma del parametro combina:.
Leslie Godwin,

3

In Swift 2.0, i metodi di protocollo minElemente maxElementdiventati SequenceType, dovresti chiamarli come:

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

L'utilizzo maxElementcome funzione come nonmaxElement(a) è disponibile al momento.

La sintassi di Swift è in evoluzione, quindi posso solo confermarlo in Xcode version7 beta6 .

Potrebbe essere modificato in futuro, quindi suggerisco di controllare il documento prima di utilizzare questi metodi.


3

Swift 3.0

Puoi provare questo codice a livello di codice.

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

Aggiornato per Swift 3/4:

Usa le seguenti semplici righe di codice per trovare il massimo dall'array;

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

-1

Puoi anche ordinare il tuo array e quindi usare array.firstoarray.last


5
Questo è computazionalmente più lento. Puoi trovare il massimo in tempo lineare.
Charlie Egan

Sono una nuovissima @CharlieEgan, puoi spiegarmi il tempo lineare o indicarmi un tutorial. Grazie mille
Saad Ghadir

leggete qualcosa sulla "complessità temporale" ( en.wikipedia.org/wiki/Time_complexity ). Vale anche la pena leggere: bigocheatsheet.com . Alcuni buoni esempi di lavoro qui: khanacademy.org/computing/computer-science/algorithms
Charlie Egan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.