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


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

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.


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)

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 :)


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

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

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) })

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.


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
}


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

-1

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.