从对象数组中删除匹配的项目?

时间:2015-04-16 14:39:15

标签: arrays swift

我有一个像这样的对象数组:

var myArr = [
  MyObject(name: "Abc", description: "Lorem ipsum 1."),
  MyObject(name: "Def", description: "Lorem ipsum 2."),
  MyObject(name: "Xyz", description: "Lorem ipsum 3.")
]

我知道我可以找到匹配的项目:

var temp = myArr.filter { $0.name == "Def" }.first

但是现在如何将其从原始myArr中删除?我希望filter.first能以某种方式返回索引,以便我可以使用removeAtIndex。或者更好的是,我想做这样的事情:

myArr.removeAll { $0.name == "Def" } // Pseudo

有什么想法吗?

5 个答案:

答案 0 :(得分:16)

您不了解的是Array是一个结构,因此是一个值类型。它不能像类实例那样进行变异。因此,您将始终在幕后创建一个新数组,即使您扩展Array以编写变异removeIf方法。

因此,使用filter和闭包条件的逻辑否定没有缺点或失去一般性:

myArr = myArr.filter { $0.name != "Def" }

例如,您可以这样写removeIf

extension Array {
    mutating func removeIf(closure:(T -> Bool)) {
        for (var ix = self.count - 1; ix >= 0; ix--) {
            if closure(self[ix]) {
                self.removeAtIndex(ix)
            }
        }
    }
}

然后你可以像这样使用它:

myArr.removeIf {$0.name == "Def"}

但事实上,这对你的时间来说是一个很大的浪费。你在这里做的事filter还没有做。您可能会myArr.removeIf语法出现,而您正在变异myArr,但您不是;你用另一个数组替换它。实际上,在该循环中对removeAtIndex的每次调用都会创建另一个数组!所以你不妨使用filter并且开心。

答案 1 :(得分:1)

使用过滤器获取对象然后遍历数组并使用myArr.removeAtIndex(index)删除每个对象。使用过滤器正是这样做的。要了解发生了什么,请阅读以下内容。 Matts回答是一种更简洁的方法来实现这一点,因为您正在测试相反的匹配,因此每个对象都会被保留,除非它与您的值匹配。

遍历临时过滤器数组

if let index = find(temp, note) {
   myArr.removeAtIndex(index)
}

答案 2 :(得分:1)

如果你想从正在迭代的数组中删除一个对象,你应该总是向后迭代,否则你将在某些时候处理那些不再有效的索引。

var myArr = [
    ["key":"value1"],
    ["key":"value2"],
    ["key":"value3"]
]

for index in stride(from: myArr.count - 1 , to: 0, by: -1){
    let x = myArr[index]
    if x["key"] == "value2"{
        myArr.removeAtIndex(index)
    }
}

答案 3 :(得分:0)

Apple已在Swift 4中添加了您想要的内容:

var phrase = "The rain in Spain stays mainly in the plain."
let vowels: Set<Character> = ["a", "e", "i", "o", "u"]
phrase.removeAll(where: { vowels.contains($0) })
// phrase == "Th rn n Spn stys mnly n th pln."

答案 4 :(得分:0)

较旧且经过全面回答的帖子和所有内容,但作为上面马特回答的轶事,对于删除单个元素的简单情况,您还可以执行以下操作:

extension Array {
    public func dropFirst(matching: (Element) -> Bool) -> ArraySlice<Element> {
        guard let at = self.firstIndex(where: matching) else { return ArraySlice(self) }
        return prefix(at) + suffix(from: at).dropFirst()
    }
}

一旦找到元素就短路

哪里:

[1, 2, 3, 4, 5].dropFirst(matching: { $0 == 3 }) // -> [1, 2, 4, 5] (ArraySlice)