Swift 通用数组函数,用于查找与项目不匹配的元素的所有索引

Swift generic array function to find all indexes of elements not matching item

Swift 3

正在尝试编写一个通用数组扩展,以获取不等于值的项目的所有索引

示例

 let arr: [String] = ["Empty", "Empty", "Full", "Empty", "Full"]
 let result: [Int] = arr.indexes(ofItemsNotEqualTo item: "Empty")
 //returns [2, 4]

我尝试创建一个通用函数:

extension Array {

    func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in self.enumerated() {
            if elem  != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

但这会给出警告:二元运算符不能应用于 "Element" 和 "T".

类型的操作数

然后我在投射元素的地方做了这个(注意 as?

extension Array {

    func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in self.enumerated() {
            if elem as? T != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

但现在似乎类型检查已经结束 window,因为如果我传入一个整数,我会得到错误的结果

 let arr: [String] = ["Empty", "Empty", "Full", "Empty", "Full"]
 let result: [Int] = arr.indexes(ofItemsNotEqualTo item: 100)
 //returns [0, 1, 2, 3, 4]

将不胜感激。

使用 reduce 函数有更好的方法吗?

您已经定义了一个泛型方法

func indexes<T: Equatable>(ofItemsNotEqualTo item: T) -> [Int]?

需要一个 T 类型的参数 Equatable,但与数组的 Element 类型无关。

因此

let arr = ["Empty", "Empty", "Full", "Empty", "Full"]
let result = arr.indexes(ofItemsNotEqualTo: 100)

编译,但 elem as? T 给出 nil(即 != item) 对于所有数组元素。

你想要的是一个只为数组定义的方法 Equatable 个元素。这可以通过约束来实现 扩展名:

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]?  {
        var result: [Int] = []
        for (n, elem) in enumerated() {
            if elem != item {
                result.append(n)
            }
        }
        return result.isEmpty ? nil : result
    }
}

实际上我不会将 return 值设为可选值。 如果所有元素都等于给定的项目,则逻辑 return 值将是空数组。

Is there a better way to do this with the reduce function?

好吧,你 可以 使用 reduce(),但这不是很有效,因为在每个迭代步骤中都会创建中间数组:

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().reduce([]) {
            .element == item ? [=13=] : [=13=] + [.offset]
        }
    }
}

你实际拥有的是 "filter + map" 操作:

extension Array where Element: Equatable {
    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().filter { [=14=].element != item }.map { [=14=].offset }
    }
}

可以使用flatMap()简化:

extension Array where Element: Equatable {

    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().flatMap { [=15=].element != item ? [=15=].offset : nil }
    }
}

示例:

let arr = ["Empty", "Empty", "Full", "Empty", "Full"]
arr.indexes(ofItemsNotEqualTo: "Full") // [0, 1, 3]

[1, 1, 1].indexes(ofItemsNotEqualTo: 1) // []

arr.indexes(ofItemsNotEqualTo: 100)
// error: cannot convert value of type 'Int' to expected argument type 'String'