Maneira correta de encontrar o máximo em uma matriz no Swift

121

Até agora, tenho uma maneira simples (mas potencialmente cara):

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

E como eu fui ensinado a fazer isso na escola:

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

Existe uma maneira melhor de obter o valor máximo de uma matriz inteira no Swift? Idealmente, algo como uma linha como a de Ruby.max

Charlie Egan
fonte
Você escreve uma extensão.
precisa saber é o seguinte
Sim, uma linha: maxElement(myArray). Veja qual é atualmente a segunda resposta (de Rudolf Adamkovic) abaixo.
Leekaiinthesky 14/05
Yo mudança que resposta aceita para esta
mattgabor
@mattymcgee Atualizei a resposta aceita.
Charlie Egan

Respostas:

299

Dado:

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
Rudolf Adamkovič
fonte
2
Funciona apenas em Comparableobjetos, portanto NSDecimalNumbernão funcionará por exemplo.
Michał Hernas 03/02
2
Sou eu ou essas funções não existem no Swift 2?
Liron Yahdav
@LironYahdav Agora eles são métodos. Fixo. Obrigado!
Rudolf Adamkovič
2
Observe que no Swift 3 eles foram renomeados para simplesmente min()e max().
21716 jemmons
1
@Jezzamon No. em Swift 3 os métodos minElemente maxElementforam renomeado para mine max. consulte: github.com/apple/swift-evolution/blob/master/proposals/… Entendo sua confusão, porque as funções livres mine maxtambém ainda existem. Veja, por exemplo, gist.github.com/lorentey/d679064cb29df4558534d619319a1d9e
jemmons
95

Atualização: Essa provavelmente deve ser a resposta aceita desde que maxElementapareceu no Swift.


Use o Todo reduce- Poderoso :

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

Similarmente:

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

reducepega um primeiro valor que é o valor inicial para uma variável interna do acumulador e aplica a função passada (aqui, é anônima) ao acumulador e a cada elemento da matriz sucessivamente, e armazena o novo valor no acumulador. O último valor do acumulador é retornado.

Jean-Philippe Pellet
fonte
1
Perfeito, exatamente o que eu estava procurando. Parece que não há muita coisa no iBook!
Charlie Egan
2
Essas são apenas técnicas gerais de programação funcional, não são específicas do Swift.
Jean-Philippe Pellet
10
@ Jean-PhilippePellet você pode realmente simplificar este apenas: nums.reduce(Int.min, max)desde max's protótipo já corresponde ao tipo que reduceestá esperando
drewag
existe uma razão pela qual isso não funciona com matrizes de duplas?
Nicholas
3
As assinaturas de função min / max correspondem à assinatura do parâmetro combine: para que você possa passar a própria função:let numMax = nums.reduce(Int.min, combine: max)
Leslie Godwin
38

Com Swift 5, Array, assim como outros Sequenceobjectos conforme protocolo ( Dictionary, Set, etc), tem dois métodos de chamadas max()e max(by:)que o elemento de retorno máxima na sequência ou nilse a sequência está vazia.


# 1 Usando Arrayo max()método

Se o tipo de elemento dentro de seus conforma seqüência de Comparableprotocolo (que pode ser String, Float, Characterou um de sua classe personalizada ou struct), você vai ser capaz de usar max()que tem a seguinte declaração :

@warn_unqualified_access func max() -> Element?

Retorna o elemento máximo na sequência.

Os seguintes códigos do Playground são exibidos 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 Arrayo max(by:)método

Se o tipo de elemento dentro da sua sequência não Comparableestiver em conformidade com o protocolo, você precisará usar max(by:)a seguinte declaração :

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

Retorna o elemento máximo na sequência, usando o predicado fornecido como comparação entre os elementos.

Os seguintes códigos do Playground são exibidos 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
fonte
No Swift 3 "maxElement" foi renomeado para "max"
Nicolai Henriksen
16

As outras respostas estão todas corretas, mas não esqueça que você também pode usar operadores de coleta, da seguinte maneira:

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

você também pode encontrar a média da mesma maneira:

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

Essa sintaxe pode ser menos clara do que algumas das outras soluções, mas é interessante ver que -valueForKeyPath:ainda pode ser usado :)

Sam
fonte
11

Você pode usar com 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
fonte
4
var numbers = [1, 2, 7, 5];    
var val = sort(numbers){$0 > $1}[0];
androabhay
fonte
2
Para mim, isso parece muito comvar myMax = sort(myArray,>)[0]
Charlie Egan
3
A classificação tem muita sobrecarga.
vy32
4

Com o Swift 1.2 (e talvez antes), agora você precisa usar:

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

Para trabalhar com valores Double, usei algo como isto:

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
fonte
1
Você também pode fazer isso let numMax = nums.reduce(-Double.infinity, combine: max), a assinatura da função max corresponde à assinatura do parâmetro combine:
Leslie Godwin
3

No Swift 2.0, os métodos de protocolo minElemente maxElementtorne - se SequenceType, você deve chamá-los como:

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

Usar maxElementcomo uma função como nãomaxElement(a) está disponível agora.

A sintaxe do Swift está em fluxo, por isso posso confirmar isso no Xcode versão7 beta6 .

Pode ser modificado no futuro, então sugiro que você verifique melhor o documento antes de usar esses métodos.

Shi XiuFeng
fonte
3

Swift 3.0

Você pode tentar esse código programaticamente.

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
fonte
0

Atualizado para Swift 3/4:

Use abaixo linhas simples de código para encontrar o máximo da matriz;

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

Você também pode classificar sua matriz e, em seguida, usar array.firstouarray.last

Saad Ghadir
fonte
5
Isso é computacionalmente mais lento. Você pode encontrar o máximo no tempo linear.
Charlie Egan #
Eu sou um @CharlieEgan muito novo, você pode explicar o tempo linear ou me indicar um tutorial. Muito obrigado
Saad Ghadir
faça algumas leituras sobre 'complexidade do tempo' ( en.wikipedia.org/wiki/Time_complexity ). Também vale a pena ler: bigocheatsheet.com . Alguns bons exemplos trabalhados aqui: khanacademy.org/computing/computer-science/algorithms
Charlie Egan