数组扩展以按值删除对象

时间:2014-07-24 16:15:25

标签: ios arrays swift

extension Array {
    func removeObject<T where T : Equatable>(object: T) {
        var index = find(self, object)
        self.removeAtIndex(index)
    }
}

但是,我在var index = find(self, object)

上收到错误消息
  

&#39; T&#39;不可转换为&#39; <&#39;

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

  

&#39; AnyObject&#39;不可转换为&#39; <&#39;

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

15 个答案:

答案 0 :(得分:165)

Swift 2 开始,可以使用协议扩展方法来实现。 removeObject()被定义为符合所有类型的方法 到RangeReplaceableCollectionType(特别是Array)if 该集合的元素是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注意到 在注释中,现在实际上可以在对模板更具限制性的泛型类型上编写方法,因此该方法 现在实际上可以定义为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)
        }
    }
}

答案 1 :(得分:65)

您无法在对模板具有更高限制性的泛型类型上编写方法。

注意:从Swift 2.0开始,您现在可以在模板上编写 更严格的方法。如果您已将代码升级到2.0,请进一步查看其他答案,以了解使用扩展程序实现此目的的新选项。

您收到错误'T' is not convertible to 'T'的原因是您实际上在方法中定义了一个与原始T无关的 new T.如果您想使用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

答案 2 :(得分:28)

简明扼要地说:

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

答案 3 :(得分:17)

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

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"]

答案 4 :(得分:13)

使用协议扩展,您可以执行此操作,

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

类的相同功能,

Swift 2

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

Swift 3

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

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

答案 5 :(得分:7)

在swift 2.0中使用协议扩展

drop: function (event, ui) {
        $(this).droppable('disable');
    }

答案 6 :(得分:4)

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

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

}

答案 7 :(得分:2)

还有一种可能性从数组中删除项目而不会出现不安全的使用,因为要删除的对象的泛型类型不能与数组的类型相同。使用选项也不是完美的方式,因为它们非常慢。因此,您可以使用类似于在排序数组时使用的闭包。

//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) -> Bool) -> Bool来缩短函数调用,然后将if-evaluation替换为equality(item),并通过array.removeFirst({ $0 == "Banana" })调用函数。

答案 8 :(得分:2)

无需扩展:

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]

答案 9 :(得分:1)

使用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 :(得分:1)

也许我不明白这个问题。

为什么这个行不通?

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

答案 11 :(得分:0)

我最终得到了以下代码。

extension Array where Element: Equatable {

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

}

答案 12 :(得分:0)

我设法通过在for循环之外实现计数来从数组[String:AnyObject]中移除[[String:AnyObject]],以表示自.find.filter不兼容的索引[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 :(得分:-1)

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

答案 14 :(得分:-4)

我能够使用它:

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