Swift泛型数组函数,用于查找与项

时间:2016-12-21 05:48:45

标签: arrays swift generics reduce

Swift 3

尝试编写一个通用数组扩展,获取DON' T等值的项目的所有索引

例如

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

但是这会发出警告:二进制运算符不能应用于类型&#34;元素&#34;的操作数。和&#34; T&#34;。

那么我在这里投了元素(请注意为?

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

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

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

非常感谢帮助。

使用 reduce 功能有更好的方法吗?

1 个答案:

答案 0 :(得分:8)

您已定义了一个通用方法

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

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

  

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

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

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

你实际拥有的是一个 &#34;过滤+地图&#34;操作:

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

可以使用flatMap()简化:

extension Array where Element: Equatable {

    func indexes(ofItemsNotEqualTo item: Element) -> [Int]  {
        return enumerated().flatMap { $0.element != item ? $0.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'