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

标签 arrays swift generics reduce

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

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

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

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

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([]) {
            $1.element == item ? $0 : $0 + [$1.offset]
        }
    }
}

你实际拥有的是 “过滤+映射”操作:

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'

关于arrays - Swift通用数组函数查找不匹配项目的元素的所有索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41255978/

相关文章:

xcode - 如何在不使用自动布局的情况下使我的应用程序适合所有 iPhone 尺寸?

java - 从接口(interface)类型而不是类类型声明,似乎不是多态的或更一般的

java - 树遍历和一阶函数

java - 是否可以在没有条件语句的情况下创建具有相同父类的随机对象?

ios - 数组索引超出范围跳过功能

javascript - 在纯 Javascript 中将输入值与 li 内容进行比较

arrays - 使用 Swift iOS 从 Firebase 中的子子项中检索所有值

javascript - 如何将数组索引拆分为该特定索引的 2 个索引

arrays - 使用rust "the trait ` std::array::LengthAtMost3 2` is not implemented"

ios - UIScrollView 速度,每秒迭代次数?