【问题标题】:Swift: Better way to remove a specific Object from an array?Swift:从数组中删除特定对象的更好方法?
【发布时间】:2019-02-03 12:39:46
【问题描述】:

我正在努力提高我的代码效率,但我脑子里放了个屁。我编写的这段代码效果很好,并且完全符合我的需要:它检查一个数组并删除一个未知索引处的对象。但我觉得有一种更好、更有效的方式来编写它。我去了 Array.remove(at:) 但这需要一个已知的索引。我正在使用大 O 表示法,不知道如何使它更容易处理。有什么想法吗?

 // create a new object array
 var sort : [MyCustomObject] = []

//iterate through my object array  

        for i in objectArray{
            if i === objectToRemove{
            }
            else{
                sort.append(i)
            }
        }

     // set old array to sort, which no longer has the unwanted object 
        self.objectArray = sort

【问题讨论】:

  • 使用index(where:) 查找要删除的对象的索引。然后,remove(at:) 将其删除。
  • 虽然这不是您问题的最佳答案,但请注意,您可以使用 !== 运算符来清理该 if 语句。

标签: arrays swift


【解决方案1】:

如果您使用 Xcode 10.0+ beta(Swift 4.2 或更高版本)进行编码,您可以使用新方法 removeAll(where:)

mutating func removeAll(where predicate: (Element) throws -> Bool) rethrows

讨论:使用此方法删除集合中的每个元素 符合特定标准的。 复杂度:O(n),其中 n 是集合的长度。

这个例子去掉了所有的奇数 来自数字数组的值:

Swift 5.2 或更高版本

extension BinaryInteger {
    var isEven: Bool { isMultiple(of: 2) }
    var isOdd: Bool { !isMultiple(of: 2) }
}

var numbers = [5, 6, 7, 8, 9, 10, 11]
numbers.removeAll(where: \.isOdd) // numbers == [6, 8, 10]
numbers

在您的情况下,请确保 MyCustomObject 符合 Equatable

objectArray.removeAll(where: { $0 == objectToRemove })

或使用符合它的属性之一作为谓词(即id: Int):

objectArray.removeAll(where: { $0.id == idToRemove })

注意:如果您不使用 Xcode 10.0+ beta (Swift 4.2),您可以实现自己的 removeAll(where:) 方法,正如您在此 answer 中看到的那样。


实现removeFirst(where:)removeLast(where:) 以避免迭代整个集合,正如@vacawama 在 cmets 中提到的那样

斯威夫特 4.1

extension RangeReplaceableCollection  {
    @discardableResult
    mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element?  {
        guard let index = try index(where: predicate) else { return nil }
        return remove(at: index)
    }
}

extension RangeReplaceableCollection where Self: BidirectionalCollection {
    @discardableResult
    mutating func removeLast(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try indices.reversed().first(where: {
            try predicate(self[$0])
        }) else { return nil }
        return remove(at: index)
    }
}

Swift 4.2 或更高版本(由 @Hamish 建议)

extension RangeReplaceableCollection {
    @discardableResult
    mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try firstIndex(where: predicate) else { return nil }
        return remove(at: index)
    }
}

extension RangeReplaceableCollection where Self: BidirectionalCollection {
    @discardableResult
    mutating func removeLast(where predicate: (Element) throws -> Bool) rethrows -> Element? {
        guard let index = try lastIndex(where: predicate) else { return nil }
        return remove(at: index)
    }
}

您还可以查看 post 以了解 remove(while:)、removeLast(while:) 和 dropLast(while:) 方法的实现。

【讨论】:

    【解决方案2】:

    您可以使用此泛型从数组中删除任何类型的对象

    extension Array where Element: Equatable {
    mutating func removeObject(object: Element)  {
        if let index = firstIndex(of: object) {
            remove(at: index)
        }
    }}
    

    【讨论】:

      【解决方案3】:

      使用firstIndex(where:)(在Swift 4.1 及更早版本中称为index(where:))使用谓词{ $0 === objectToRemove } 在数组中搜索对象,然后在数组上调用remove(at:) 以删除它:

      if let idx = objectArray.firstIndex(where: { $0 === objectToRemove }) {
          objectArray.remove(at: idx)
      }
      

      这允许您搜索您的对象,无论它是否为Equatable

      【讨论】:

      • 请注意,firstIndex(where:) 仍处于测试阶段(以前称为index(where:)
      • @LeoDabus,感谢您的留言。我找不到index(where:) Apple 文档的链接。
      • 我也找不到。
      • 最后,您的答案是最干净、最容易实现的代码,无需使我的 CustomObject Equatable。谢谢!我还检查了苹果文档中的 index(where:) 并且找不到任何东西。但它有效!
      • 尽管 Swift 4.1,index(where:) 是电话。 Apple 刚刚在 Swift 4.2 发布之前更新了文档(现在很快)。
      【解决方案4】:

      斯威夫特 4

      使用enumerated(),您将获得索引:

      var array = ["a","b","c"]
      let objectToRemove = "b"
      for (index, value) in array.enumerated() {
          if value == objectToRemove {
              array.remove(at: index)
          }
      }
      print(array) // Prints: ["a","c"]
      

      编辑:

      或者也许更好,使用过滤器:

      var array = ["a","b","c"]
      let objectToRemove = "b"
      array = array.filter { $0 != objectToRemove } // Keeps only what is
                                                    // different from the 
                                                    // objectToRemove
      print(newArray)  // Prints: ["a","c"]
      

      【讨论】:

      • 你是对的!我将仅以for in 为例,因为它对初学者来说更容易理解;-) 非常感谢 Leo Dabus!
      • enumerate 和删除将导致Index out of range
      【解决方案5】:

      你可以试试

      if let ind = objectArray.index(of:objectToRemove) {
        objectArray.remove(at:ind)
      }
      

      【讨论】:

      • 谢谢!我试过了,但我得到'不能调用'index',参数列表类型为'(of:MyCustomObject)'。我认为这不起作用,因为它是一个自定义对象?
      • class YourClass:Equatable { }
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-11-02
      • 2018-10-03
      • 2016-09-29
      • 2017-04-13
      • 1970-01-01
      • 1970-01-01
      • 2013-12-13
      相关资源
      最近更新 更多