在 Swift 数组中查找最大值的索引

16

我有两个数组,一个用于存储玩家名称,另一个用于存储分数。例如:

var players = ["Bill", "Bob", "Sam", "Dave"]
var scores = [10,15,12,15]

我可以通过以下方式找到最高分数的索引(以及获胜者的姓名):

let highScore = scores.max()
let winningPlayerIndex = scores.index(of: highScore!)
let winningPlayer = players[winningPlayerIndex!]

如果只有一个玩家具有最高分数,则此方法有效,但是如何返回所有与最大值相等的值的多个索引(例如,在本例中为1和3)?我需要将这些索引映射回到玩家数组以提取所有具有最高得分的玩家的名称。或者有更好的方法来完成所有这些操作吗?


8
你应该为玩家名称和得分分别创建一个“struct”结构体,然后只需使用一个包含这些结构体的单一数组即可。 - Hamish
谢谢@Hamish - 看起来在这个例子中结构体是正确的选择。我打算将整个东西移动到coredata/entities,但首先只是尝试弄清楚一些功能。 - mallowman
6个回答

15

接受的答案不能推广到比较元素上计算出的值。获取最小/最大值和索引的最简单和最有效的方法是对列表进行枚举,并使用元组(偏移量,元素)进行操作:

struct Player {
    let name: String
    let stats: [Double]
}

let found = players.enumerated().max(by: { (a, b) in
   battingAvg(a.element.stats) < battingAvg(b.element.stats)
})

print(found.element.name, found.offset)  // "Joe", 42

一般情况下你不应该通过相等性比较来依赖浮点数值,即使你可以这样做,如果计算很昂贵,你也不希望重复它以在列表中查找项目。


battingAvg()是什么?它是自定义方法吗? - aznelite89
1
是的,那只是一个任意计算值的示例。(这是美国棒球的一件事情。) - Pat Niemeyer

7
要回答标题中的问题 - 在一个(单独的)数组中查找最大值的索引:
```python array.index(max(array)) ```
这将返回最大值在数组中的索引。
extension Array where Element: Comparable {
   var indexOfMax: Index? {
      guard var maxValue = self.first else { return nil }
      var maxIndex = 0

      for (index, value) in self.enumerated() {
         if value > maxValue {
            maxValue = value
            maxIndex = index
         }
     }

     return maxIndex
   }
}

如果数组为空,则该扩展将返回nil。否则,它首先假设第一个值是最大值,然后迭代所有值,更新索引和值以查找任何更大的值,最后返回结果。


10
这段代码的意思是:找到数组a中值最大的元素的索引,其中这个元素比它后面的元素都要大。 - Dave Abrahams

7
您需要使用自定义的classstructure,并创建一个数组,然后找到最高分数,接着用最高分数过滤您的数组。
struct Player {
    let name: String
    let score: Int
}

现在创建这个玩家结构的数组。
var players = [Player(name: "Bill", score: 10), Player(name: "Bob", score: 15), Player(name: "Sam", score: 12), Player(name: "Dave", score: 15)]

let maxScore = players.max(by: { $0.0.score < $0.1.score })?.score ?? 0

使用以下方式在数组上过滤以获取拥有最大分数的玩家数组。
let allPlayerWithMaxScore = players.filter { $0.score == maxScore }

要获取得分高的球员的索引数组,可以使用以下方法对数组进行筛选。
let indexForPlayerWithMaxScore = players.indices.filter { players[$0].score == maxScore }
print(indexForPlayerWithMaxScore) //[1, 3]

谢谢@Nirav - 这正是我需要的。maxScore函数是如何工作的?我无法完全理解它(我对此还很陌生!),而且语法/结构与我在xcode中输入时看到的不同。 - mallowman
@mallowman 欢迎啊,伙计 :) 我们使用的是 max(by:),你可以在这里获取更多详细信息 https://developer.apple.com/reference/swift/array/2294243-max - Nirav D
1
不错。干杯。 - mallowman
1
你也可以使用 players.lazy.map{ $0.score }.max() 来获取最高分数 :) - Hamish

3

您可以使用集合的min方法将其索引与元素压缩,并传递一个谓词来比较元素的最小值。获取结果并提取元组的索引:

let numbers = [2, 4, 4, 2, 3, 1]
let minIndex = zip(numbers.indices, numbers).min(by: { $0.1 < $1.1 })?.0  // 5
let maxIndex = zip(numbers.indices, numbers).max(by: { $0.1 < $1.1 })?.0  // 1

作为一个元素可比较的扩展:
extension Collection where Element: Comparable {
    func firstIndexOfMaxElement() -> Index? {
        zip(indices, self).max(by: { $0.1 < $1.1 })?.0
    }
    func firstIndexOfMinElement() -> Index? {
        zip(indices, self).min(by: { $0.1 < $1.1 })?.0
    }
}

使用方法:
numbers.firstIndexOfMinElement()  // 5

如果您需要找到最大或最小属性:
extension Collection {
    func firstIndexOfMaxElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
        zip(indices, self).max(by: { predicate($0.1) < predicate($1.1) })?.0
    }
    func firstIndexOfMinElement<T: Comparable>(_ predicate: (Element) -> T) -> Index? {
        zip(indices, self).min(by: { predicate($0.1) < predicate($1.1) })?.0
    }
}

使用方法:
struct Product {
    let price: Int
}

let products: [Product] = [.init(price: 2),
                           .init(price: 4),
                           .init(price: 4),
                           .init(price: 2),
                           .init(price: 3),
                           .init(price: 1),]

let minPrice = products.firstIndexOfMinElement(\.price)    // 5

返回最大和最小元素及其索引:

extension Collection where Element: Comparable {
    func maxElementAndIndices() -> (indices: [Index], element: Element)? {
        guard let maxValue = self.max() else { return nil }
        return (indices.filter { self[$0] == maxValue }, maxValue)
    }
    func minElementAndIndices() -> (indices: [Index], element: Element)? {
        guard let minValue = self.min() else { return nil }
        return (indices.filter { self[$0] == minValue }, minValue)
    }
}

对于自定义的结构/类,有相应的方法:

extension Collection {
    func maxElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)]  {
        guard let maxValue = self.max(by:{ predicate($0) < predicate($1)}) else { return [] }
        return zip(indices, self).filter { predicate(self[$0.0]) == predicate(maxValue) }
    }
    func minElementsAndIndices<T: Comparable>(_ predicate: (Element) -> T) -> [(index: Index, element: Element)] {
        guard let minValue = self.min(by:{ predicate($0) < predicate($1)}) else { return [] }
        return zip(indices, self).filter { predicate(self[$0.0]) == predicate(minValue) }
    }
}

使用方法:
let maxNumbers = numbers.maxElementAndIndices()  // ([1, 2], element 4)
let minNumbers =  numbers.minElementAndIndices()  // ([5], element 1)

let maxPriceIndices = products.maxElementsAndIndices(\.price)    // [(index: 1, element: Product(price: 4)), (index: 2, element: Product(price: 4))]
let minPriceIndices = products.minElementsAndIndices(\.price)    // [(index: 5, element: __lldb_expr_22.Product(price: 1))]

3

如果你有两个数组,并需要从第一个数组中找到最高分数以便从第二个数组中获取名称,那么我建议使用 zip 高阶函数将两个数组合并成一个,然后从中取得最大值。

因此,你的数据看起来应该是这样的:

let players = ["Bill", "Bob", "Sam", "Dave"]
let scores = [10,15,12,15]

let data = zip(players, scores)

// max score
let maxResult = data.max(by: ({ $0.1 < $1.1 }))?.1 ?? 0
// outputs 15

// leaders
let leaders = data.filter { $0.1 >= maxResult }.map { "\($0.0) - \($0.1)" }
// outputs ["Bob - 15", "Dave - 15"]


0

有几种方法可以解决你的问题,你可以通过保存 scores.max() 的索引并遍历 players 列表来解决此问题,并使用 zip 函数:

var max_score = scores.max()
var players_and_score = zip(players, scores)
for player in players_and_score{
    if player.1 == max_score{
       print(player.0)
    }
}

1
你应该先计算 max(),否则性能会非常差。 - Sulthan
谢谢@Edison - 这很有效,也能满足我的需求。我之前不知道有zip函数。 - mallowman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接