【问题标题】:Array extension to remove object by value按值删除对象的数组扩展
【发布时间】:2014-09-16 07:37:23
【问题描述】:
extension Array {
    func removeObject<T where T : Equatable>(object: T) {
        var index = find(self, object)
        self.removeAtIndex(index)
    }
}

但是,var index = find(self, object) 出现错误

'T' 不能转换为 'T'

我也尝试了这个方法签名:func removeObject(object: AnyObject),但是,我得到了同样的错误:

'AnyObject' 不能转换为 'T'

这样做的正确方法是什么?

【问题讨论】:

标签: ios arrays swift


【解决方案1】:

您不能在对模板有更多限制的泛型类型上编写方法。

注意:从 Swift 2.0 开始,您现在可以编写 对模板有更多限制的方法。如果您已将代码升级到 2.0,请参阅其他答案进一步了解使用扩展实现此功能的新选项。

您收到错误'T' is not convertible to 'T' 的原因是您实际上在您的方法中定义了一个与原始 T 完全无关的 new T。如果您想在您的方法,你可以这样做而不用在你的方法中指定它。

您收到第二个错误'AnyObject' is not convertible to 'T' 的原因是 T 的所有可能值并非所有类。对于要转换为 AnyObject 的实例,它必须是类(不能是结构、枚举等)。

最好的办法是让它成为一个接受数组作为参数的函数:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) {
}

或者,您可以通过返回一个副本使您的方法更加线程安全和可重用,而不是修改原始数组:

func arrayRemovingObject<T : Equatable>(object: T, fromArray array: [T]) -> [T] {
}

作为一种我不推荐的替代方法,如果存储在数组中的类型无法转换为方法模板(即相等的),您可以让您的方法静默失败。 (为清楚起见,我使用 U 而不是 T 作为方法的模板):

extension Array {
    mutating func removeObject<U: Equatable>(object: U) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            if let to = objectToCompare as? U {
                if object == to {
                    index = idx
                }
            }
        }

        if(index != nil) {
            self.removeAtIndex(index!)
        }
    }
}

var list = [1,2,3]
list.removeObject(2) // Successfully removes 2 because types matched
list.removeObject("3") // fails silently to remove anything because the types don't match
list // [1, 3]

编辑要克服静默失败,您可以将成功返回为布尔值:

extension Array {
  mutating func removeObject<U: Equatable>(object: U) -> Bool {
    for (idx, objectToCompare) in self.enumerate() {  //in old swift use enumerate(self) 
      if let to = objectToCompare as? U {
        if object == to {
          self.removeAtIndex(idx)
          return true
        }
      }
    }
    return false
  }
}
var list = [1,2,3,2]
list.removeObject(2)
list
list.removeObject(2)
list

【讨论】:

  • 在这里查看我的答案:stackoverflow.com/a/24939242/458960 为什么我可以这样做而不使用find 方法?
  • 您的方法容易受到运行时崩溃的影响。使用我的函数,编译器将完全防止这种情况发生。
  • @Isuru 此方法适用于任何实现Equatable 协议的对象。 UIView 这样做是的,它将与 UIViews 一起使用
  • 哇,写一个for循环来删除一个元素,回到90年代了!
  • 在最新的 swift 中。 enumerate(self) 必须修复为 self.enumerate()
【解决方案2】:

我能够让它工作:

extension Array {
    mutating func removeObject<T: Equatable>(object: T) {
        var index: Int?
        for (idx, objectToCompare) in enumerate(self) {
            let to = objectToCompare as T
            if object == to {
                index = idx
            }
        }

        if(index) {
            self.removeAtIndex(index!)
        }
    }
}

【讨论】:

  • 比较if(index)无效
【解决方案3】:

简明扼要:

func removeObject<T : Equatable>(object: T, inout fromArray array: [T]) 
{
    var index = find(array, object)
    array.removeAtIndex(index!)
}

【讨论】:

  • 这很酷。当然也可以不用inout 来完成。即使inout 完好无损,我认为也可以使用array = array.filter() { $0 != object }
  • 注意使用强制解包索引,它可能为 nil。改为“if let ind = index {array.removeAtIndex(ind)}”
【解决方案4】:

还有另一种可能性,即从数组中删除一项而不会造成不安全的使用,因为要删除的对象的泛型类型不能与数组的类型相同。使用可选项也不是完美的方法,因为它们非常慢。因此,您可以使用像在对数组进行排序时已经使用的闭包一样。

//removes the first item that is equal to the specified element
mutating func removeFirst(element: Element, equality: (Element, Element) -> Bool) -> Bool {
    for (index, item) in enumerate(self) {
        if equality(item, element) {
            self.removeAtIndex(index)
            return true
        }
    }
    return false
}

当您使用此函数扩展 Array 类时,您可以通过执行以下操作删除元素:

var array = ["Apple", "Banana", "Strawberry"]
array.removeFirst("Banana") { $0 == $1 } //Banana is now removed

但是,您甚至可以删除具有相同内存地址的元素(当然,仅适用于符合AnyObject 协议的类):

let date1 = NSDate()
let date2 = NSDate()
var array = [date1, date2]
array.removeFirst(NSDate()) { $0 === $1 } //won't do anything
array.removeFirst(date1) { $0 === $1 } //array now contains only 'date2'

好处是,您可以指定要比较的参数。例如,当您有一个数组数组时,您可以将相等闭包指定为{ $0.count == $1.count },并且从数组中删除与要删除的数组大小相同的第一个数组。

您甚至可以通过将函数设置为 mutating func removeFirst(equality: (Element) -&gt; Bool) -&gt; Bool 来缩短函数调用,然后将 if 评估替换为 equality(item) 并通过 array.removeFirst({ $0 == "Banana" }) 调用函数。

【讨论】:

  • 由于==是一个函数,你也可以像这样调用任何实现==的类型(如String、Int等):array.removeFirst("Banana", equality:==)
  • @AvielGross 我认为这是 Swift 2 中的新功能 - 如果您愿意,请随时相应地编辑答案
【解决方案5】:

阅读完以上所有内容后,我认为最好的答案是:

func arrayRemovingObject<U: Equatable>(object: U, # fromArray:[U]) -> [U] {
  return fromArray.filter { return $0 != object }
}

示例:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = arrayRemovingObject("Cat", fromArray:myArray )

Swift 2 (xcode 7b4) 数组扩展:

extension Array where Element: Equatable {  
  func arrayRemovingObject(object: Element) -> [Element] {  
    return filter { $0 != object }  
  }  
}  

示例:

var myArray = ["Dog", "Cat", "Ant", "Fish", "Cat"]
myArray = myArray.arrayRemovingObject("Cat" )

Swift 3.1 更新

现在 Swift 3.1 已经发布了。下面是一个扩展,它提供了详尽的、快速的、变异的和创建的变体。

extension Array where Element:Equatable {
    public mutating func remove(_ item:Element ) {
        var index = 0
        while index < self.count {
            if self[index] == item {
                self.remove(at: index)
            } else {
                index += 1
            }
        }
    }

    public func array( removing item:Element ) -> [Element] {
        var result = self
        result.remove( item )
        return result
    }
}

样本:

// Mutation...
      var array1 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      array1.remove("Cat")
      print(array1) //  ["Dog", "Turtle", "Socks"]

// Creation...
      let array2 = ["Cat", "Dog", "Turtle", "Cat", "Fish", "Cat"]
      let array3 = array2.array(removing:"Cat")
      print(array3) // ["Dog", "Turtle", "Fish"]

【讨论】:

  • 这不是返回一个全新的数组实例吗?
  • 是的。这是一种更实用的风格。 YMMV。
  • 我倾向于同意功能样式,除非在这种情况下,filter 函数已经为您处理了该功能。这似乎重复了功能。但仍然是一个很好的答案:]
【解决方案6】:

Swift 2 开始,这可以通过协议扩展方法来实现。 removeObject() 被定义为所有类型符合的方法 到RangeReplaceableCollectionType(尤其是Array)如果 该集合的元素是Equatable:

extension RangeReplaceableCollectionType where Generator.Element : Equatable {

    // Remove first collection element that is equal to the given `object`:
    mutating func removeObject(object : Generator.Element) {
        if let index = self.indexOf(object) {
            self.removeAtIndex(index)
        }
    }
}

例子:

var ar = [1, 2, 3, 2]
ar.removeObject(2)
print(ar) // [1, 3, 2]

Swift 2 / Xcode 7 beta 2 的更新: 正如 Airspeed Velocity 所注意到的 在 cmets 中,现在实际上可以在对模板有更多限制的泛型类型上编写方法,因此该方法 现在实际上可以定义为Array的扩展:

extension Array where Element : Equatable {
    
    // ... same method as above ...
}

协议扩展仍然具有适用于 更大的类型集。

Swift 3 更新:

extension Array where Element: Equatable {
    
    // Remove first collection element that is equal to the given `object`:
    mutating func remove(object: Element) {
        if let index = index(of: object) {
            remove(at: index)
        }
    }
}

Swift 5 更新:

extension Array where Element: Equatable {
    
    /// Remove first collection element that is equal to the given `object` or `element`:
    mutating func remove(element: Element) {
        if let index = firstIndex(of: element) {
            remove(at: index)
        }
    }
}

【讨论】:

  • 完美,你一定喜欢 Swift (2)。我真的很喜欢随着时间的推移,更多的事情变得可能,事情变得简单
  • 好点,在许多方面,答案在技术上仍然是正确的,只是不再是惯用的,更糟糕​​的是——人们会来,阅读答案,认为免费函数是正确的方法解决它,因为它是一个高度评价的答案。相当丑陋的场景。将发布到元。
  • @AirspeedVelocity:哇,我错过了。是否包含在发行说明中?
  • 如果您想要与 ObjC 相同的功能(即删除所有匹配的对象而不是仅删除第一个),您可以将“if”更改为“while”
  • Swift 3 版本很棒,但我会将其声明稍微重命名为 remove(object: Element) 以符合 Swift API design guidelines 并避免冗长。我已经提交了反映这一点的修改。
【解决方案7】:

在 swift 2.0 中使用协议扩展

extension _ArrayType where Generator.Element : Equatable{
    mutating func removeObject(object : Self.Generator.Element) {
        while let index = self.indexOf(object){
            self.removeAtIndex(index)
        }
    }
}

【讨论】:

    【解决方案8】:

    在 Swift 2 中的实现:

    extension Array {
      mutating func removeObject<T: Equatable>(object: T) -> Bool {
        var index: Int?
        for (idx, objectToCompare) in self.enumerate() {
          if let toCompare = objectToCompare as? T {
            if toCompare == object {
              index = idx
              break
            }
          }
        }
        if(index != nil) {
          self.removeAtIndex(index!)
          return true
        } else {
          return false
        }
      }
    }
    

    【讨论】:

      【解决方案9】:

      使用indexOf 代替forenumerate

      extension Array where Element: Equatable {
      
         mutating func removeElement(element: Element) -> Element? {
            if let index = indexOf(element) {
               return removeAtIndex(index)
            }
            return nil
         }
      
         mutating func removeAllOccurrencesOfElement(element: Element) -> Int {
             var occurrences = 0
             while true {
                if let index = indexOf(element) {
                   removeAtIndex(index)
                   occurrences++
                } else {
                   return occurrences
                }
             }
         }   
      }
      

      【讨论】:

        【解决方案10】:

        通过协议扩展,您可以做到这一点,

        extension Array where Element: Equatable {
            mutating func remove(object: Element) {
                if let index = indexOf({ $0 == object }) {
                    removeAtIndex(index)
                }
            }
        }
        

        类的功能相同,

        斯威夫特 2

        extension Array where Element: AnyObject {
            mutating func remove(object: Element) {
                if let index = indexOf({ $0 === object }) {
                    removeAtIndex(index)
                }
            }
        }
        

        斯威夫特 3

        extension Array where Element: AnyObject {
            mutating func remove(object: Element) {
                if let index = index(where: { $0 === object }) {
                     remove(at: index)
                }
            }
        }
        

        但是如果一个类实现了 Equatable 它就会变得模棱两可并且编译器会抛出一个错误。

        【讨论】:

        • 我收到了Binary operator '===' cannot be applied to two elements of type '_' and 'Element'
        【解决方案11】:

        我终于得到了以下代码。

        extension Array where Element: Equatable {
        
            mutating func remove<Element: Equatable>(item: Element) -> Array {
                self = self.filter { $0 as? Element != item }
                return self
            }
        
        }
        

        【讨论】:

          【解决方案12】:

          由于.find.filter[String:AnyObject] 不兼容,我设法通过在for 循环之外实现计数来表示索引,从而从数组[[String:AnyObject]] 中删除[String:AnyObject]

          let additionValue = productHarvestChoices[trueIndex]["name"] as! String
          var count = 0
          for productHarvestChoice in productHarvestChoices {
            if productHarvestChoice["name"] as! String == additionValue {
              productHarvestChoices.removeAtIndex(count)
            }
            count = count + 1
          }
          

          【讨论】:

            【解决方案13】:

            如何使用过滤?即使使用 [AnyObject],以下内容也能很好地工作。

            import Foundation
            extension Array {
                mutating func removeObject<T where T : Equatable>(obj: T) {
                    self = self.filter({$0 as? T != obj})
                }
            
            }
            

            【讨论】:

              【解决方案14】:

              也许我没听懂这个问题。

              为什么这不起作用?

              import Foundation
              extension Array where Element: Equatable {
                  mutating func removeObject(object: Element) {
                      if let index = self.firstIndex(of: object) {
                          self.remove(at: index)
                      }
                  }
              }
              
              var testArray = [1,2,3,4,5,6,7,8,9,0]
              testArray.removeObject(object: 6)
              let newArray = testArray
              
              var testArray2 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
              testArray2.removeObject(object: "6")
              let newArray2 = testArray2
              

              【讨论】:

                【解决方案15】:

                无需扩展:

                var ra = [7, 2, 5, 5, 4, 5, 3, 4, 2]
                
                print(ra)                           // [7, 2, 5, 5, 4, 5, 3, 4, 2]
                
                ra.removeAll(where: { $0 == 5 })
                
                print(ra)                           // [7, 2, 4, 3, 4, 2]
                
                if let i = ra.firstIndex(of: 4) {
                    ra.remove(at: i)
                }
                
                print(ra)                           // [7, 2, 3, 4, 2]
                
                if let j = ra.lastIndex(of: 2) {
                    ra.remove(at: j)
                }
                
                print(ra)                           // [7, 2, 3, 4]
                

                【讨论】:

                  【解决方案16】:

                  您的问题是 T 无论如何都与您的数组类型无关,例如您可以拥有

                  var array = [1,2,3,4,5,6]
                  
                  array.removeObject(object:"four")
                  

                  "six" 是 Equatable,但它不是一个可以和 Integer 比较的类型,如果你把它改为

                  var array = [1,2,3,4,5,6]
                  
                  extension Array where Element : Equatable {
                      mutating func removeObject(object: Element) {
                          filter { $0 != object }
                      }
                  }
                  
                  array.removeObject(object:"four")
                  

                  它现在在调用 removeObject 时会产生错误,原因很明显它不是字符串数组,要删除 4 你可以

                  array.removeObject(object:4)
                  

                  您遇到的其他问题是它是一个自我修改的结构,因此必须将方法标记为如此,并且您在顶部对它的引用必须是 var

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多