数组扩展按值删除对象


140
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”

正确的方法是什么?


尝试T where从您的方法声明中删除。所以就这样func removeObject<T: Equatable>。这个问题是相关的:stackoverflow.com/questions/24091046/...
ahruss

Answers:


165

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]

更新斯威夫特2 / Xcode的7测试版2:空速速度在评论中发现,现在其实可以写一个通用的类型,它是在模板更严格的方法,因此该方法可现在居然被定义为一个扩展的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
完美,您必须爱Swift(2)。我真的很喜欢随着时间的推移,更多的事情变成可能,事情变得简单
Kametrixom 2015年

1
好一点,在很多方面,答案在技术上仍然是正确的,而不再是惯用的,甚至更糟–人们会来,阅读答案,认为自由函数是解决它的正确方法,因为它是一个高度评价的答案。相当丑陋的场景。将发布到meta。
空速速度

1
@AirspeedVelocity:哇,我错过了。发行说明中涵盖了它吗?
Martin R

1
如果您想要与ObjC相同的功能(即删除所有匹配的对象而不是仅删除第一个对象),则可以将“ if”更改为“ while”
powertoold 2015年

2
Swift 3版本很棒,但remove(object: Element)为了符合Swift API设计指南并避免冗长,我将其声明重命名为。我已经提交了反映此内容的编辑。
swiftcode '16

66

您不能以对模板有更多限制的通用类型编写方法。

:如雨燕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

在这里查看我的答案:stackoverflow.com/a/24939242/458960为什么我可以这种方式而不使用该find方法?
雪人2014年

您的方法容易出现运行时崩溃。使用我的函数,编译器将完全阻止这种情况的发生。
drewag

1
@Isuru此方法可用于实现Equatable协议的任何对象。UIView这样做是的,它将可以与
UIViews

4
哇,写一个for循环来删除一个元素,回到90年代了!
Zorayr

5
以最新的速度。enumerate(self)必须修复self.enumerate()
TomSawyer

28

简明扼要:

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

2
这很酷。当然,也可以不使用inout。我认为即使inout完整无缺,也可以使用array = array.filter() { $0 != object }
Dan Rosenstark 2015年

11
注意使用强制解开索引,该索引可能为零。更改为“ if let ind = index {array.removeAtIndex(ind)}”
HotJard 2015年

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

这不返回数组的全新实例吗?
pxpgraphics

是。这是一种更加实用的样式。YMMV。
12

除了在这种情况下,当filter函数已经为您处理该功能时,我倾向于同意该函数样式。这似乎是重复的功能。不过,这是一个不错的答案:]
pxpgraphics,2016年

13

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

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,它将变得模棱两可,并且编译器将引发错误。


1
我要买一Binary operator '===' cannot be applied to two elements of type '_' and 'Element'
双鞋

6

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

4

怎么使用过滤?即使使用[AnyObject],以下代码也可以很好地工作。

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

}

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


由于==是一个函数,因此您也可以为实现的任何类型==(例如String,Int等)这样调用它:array.removeFirst("Banana", equality:==)
Aviel Gross 2015年

@AvielGross我想这是Swift 2的新功能-如果您愿意,可以随时编辑答案
borchero 2015年

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]

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

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

0

我最终以以下代码结束。

extension Array where Element: Equatable {

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

}

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
}

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

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

比较if(index)无效
juanjo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.