Array extension to remove object by value

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

However, I get an error on var index = find(self, object)

'T' is not convertible to 'T'

I also tried with this method signature: func removeObject(object: AnyObject), however, I get the same error:

'AnyObject' is not convertible to 'T'

What is the proper way to do this?

76282 次浏览

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

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

得到错误 'T' is not convertible to 'T'的原因是您实际上在方法中定义了一个与原始 T 完全无关的 新的T。如果希望在方法中使用 T,则可以这样做,而无需在方法中指定它。

得到第二个错误 'AnyObject' is not convertible to 'T'的原因是 T 的所有可能值并不都是类。要将实例转换为 AnyObject,它必须是一个类(不能是 struct、 enum 等)。

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

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

我可以让它工作:

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

简明扼要地说:

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

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

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

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

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更新

既然斯威夫特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"]

至于 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:,因为注意到了空速 在注释中,现在实际上可以在泛型类型上编写对模板限制更多的方法,所以该方法 现在实际上可以被定义为 Array的延伸:

extension Array where Element : Equatable {
    

// ... same method as above ...
}

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

斯威夫特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)
}
}
}

迅捷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.0中使用协议扩展

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

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

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

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

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

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

我最终得到了以下代码。

extension Array where Element: Equatable {


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


}

如何使用过滤? 下面的方法即使在[ AnyObject ]中也能很好地工作。

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


}

也许我没明白你的问题。

为什么不行呢?

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

无需延长:

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]

您的问题是 T 与数组的类型无关,例如,您可以使用

var array = [1,2,3,4,5,6]


array.removeObject(object:"four")

“ six”是可等价的,但是它不是一个可以与 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