替换数组中某个项目(如果存在)的简单方法,如果不存在则追加

时间:2019-03-20 15:11:32

标签: arrays swift

Swift 4.2

我有多个函数可以替换数组中的对象或结构(如果存在),如果不存在,则将其添加。

func updateFruit(_ fruit: Fruit)
{
    if let idx = fruitArray.firstIndex(where: { $0.id == fruit.id })
    {
        fruitArray[idx] = fruit
    }
    else
    {
        fruitArray.append(fruit)
    }
}

很明显,我可以将其扩展为Array:

extension Array
{
    mutating func replaceOrAppend(_ item: Element, whereFirstIndex predicate: (Element) -> Bool)
    {
        if let idx = self.firstIndex(where: predicate)
        {
            self[idx] = item
        }
        else
        {
            append(item)
        }
    }
}

但是,有没有更简单,更轻松的方式来表达这一点?最好使用闭包或内置函数。

注意:当前实现不允许使用集合。

3 个答案:

答案 0 :(得分:1)

考虑到您经常检查$0.<prop> == newthing.<prop>的用例,您可以通过添加以下内容来进一步提高这一点:

mutating func replaceOrAppend<Value>(_ item: Element, 
                                     firstMatchingKeyPath keyPath: KeyPath<Element, Value>)
    where Value: Equatable
{
    let itemValue = item[keyPath: keyPath]
    replaceOrAppend(item, whereFirstIndex: { $0[keyPath: keyPath] == itemValue })
}

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

struct Student {
    let id: Int
    let name: String
}

let alice0 = Student(id: 0, name: "alice")
let alice1 = Student(id: 1, name: "alice")
let bob = Student(id: 0, name: "bob")

var array = [alice0]

array.replaceOrAppend(alice1, firstMatchingKeyPath: \.name) // [alice1]
array.replaceOrAppend(bob, firstMatchingKeyPath: \.name)    // [alice1, bob]

当然,如果您经常执行此操作,则可以继续抬起头来。

protocol Identifiable {
    var id: Int { get }
}

extension Student: Identifiable {}

extension Array where Element: Identifiable {
    mutating func replaceOrAppendFirstMatchingID(_ item: Element)
    {
        replaceOrAppend(item, firstMatchingKeyPath: \.id)
    }
}

array.replaceOrAppendFirstMatchingID(alice0) // [alice1, alice0]

答案 1 :(得分:0)

我建议使用protocol创建Replacable replaceValue,它将代表我们可以用来枚举对象的标识符。

protocol Replacable {
    var replaceValue: Int { get }
}

现在我们可以创建Array的扩展名,但是现在我们可以从这样的示例代码中删除谓词

extension Array where Element: Replacable {
    mutating func replaceOrAppend(_ item: Element) {
        if let idx = self.firstIndex(where: { $0.replaceValue == item.replaceValue }) {
            self[idx] = item
        }
        else {
            append(item)
        }
    }
}

由于Set不是有序集合,因此如果集合包含对象,我们可以简单地删除它并插入新值

extension Set where Element: Replacable {
    mutating func replaceOrAppend(_ item: Element) {
        if let existItem = self.first(where: { $0.replaceValue == item.replaceValue }) {
            self.remove(existItem)
        }
        self.insert(item)
    }
}

答案 2 :(得分:0)

假设您的类型是相等的,这是一个通用扩展名:

extension RangeReplaceableCollection where Element: Equatable {

    mutating func addOrReplace(_ element: Element) {
        if let index = self.firstIndex(of: element) {
            self.replaceSubrange(index...index, with: [element])
        }
        else {
            self.append(element)
        }
    }
}

尽管如此,请记住,我(和您)的功能只会替换一个匹配项。

全面工作场所测试:

Playgrounds Test