Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Arrays 替换数组中某个项(如果存在)的简单方法,如果不存在则追加该项';T_Arrays_Swift - Fatal编程技术网

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

Arrays 替换数组中某个项(如果存在)的简单方法,如果不存在则追加该项';T,arrays,swift,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) } }

Swift 4.2

我有多个函数来替换数组中的对象或结构(如果存在),如果不存在,则添加它

func updateFruit(_ fruit: Fruit)
{
    if let idx = fruitArray.firstIndex(where: { $0.id == fruit.id })
    {
        fruitArray[idx] = fruit
    }
    else
    {
        fruitArray.append(fruit)
    }
}
显然,我可以将其扩展到阵列上:

extension Array
{
    mutating func replaceOrAppend(_ item: Element, whereFirstIndex predicate: (Element) -> Bool)
    {
        if let idx = self.firstIndex(where: predicate)
        {
            self[idx] = item
        }
        else
        {
            append(item)
        }
    }
}
然而,有没有一种更简单、更容易的表达方式?最好使用闭包或内置函数


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

我建议使用
replaceValue
创建
协议
Replacable
,该协议将表示我们可以用来枚举通过对象的标识符

protocol Replacable {
    var replaceValue: Int { get }
}
现在我们可以创建对
数组的扩展,但现在我们可以从下面的示例代码中删除谓词

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

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

protocol Replacable {
    var replaceValue: Int { get }
}
现在我们可以创建对
数组的扩展,但现在我们可以从下面的示例代码中删除谓词

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

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

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

不过,请记住,我的(和你的)函数只会替换一个匹配项

全工作操场测试:


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

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

不过,请记住,我的(和你的)函数只会替换一个匹配项

全工作操场测试:


给定您的用例,在该用例中您总是检查
$0。==newthing.
,您可以通过添加以下内容来进一步提升此功能:

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 })
}
当然,如果你经常这样做,你可以不断地提升

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]

给定您的用例,在该用例中您总是检查
$0.==newthing.
,您可以通过添加以下内容来进一步提升此功能:

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 })
}
当然,如果你经常这样做,你可以不断地提升

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]

你的代码很简单:)仅供参考-因为你实际上是在寻找更多的工作代码的代码审查,你可能希望看看这个网站。如果你最终在那里发布了你的问题,请删除这一个。你可以让你的
水果符合
平等的
,从而让你的生活“更轻松”。这样做不需要谓词。如果你想使用一个
集合
只需使它也符合
可散列
。@LeoDabus我喜欢这个解决方案。当你说你有“多个函数”时,其他函数是什么样子的?我怀疑你把这段代码写错了方向。它们是否都有类似于
$0.id==newthing.id
的内容,或者它们是否有其他谓词?所有谓词都是
$0.==新事物。
(即使不仅仅是
id
)?还是这些谓词要复杂得多?通用代码必须总是从它的调用方式开始,否则你会走上错误的道路。你的代码简单易懂:)仅供参考-因为你实际上是在寻找工作代码的更多代码审查,你可能希望查看该网站。如果你最终在那里发布了你的问题,请删除这一个。你可以让你的
水果符合
平等的
,从而让你的生活“更轻松”。这样做不需要谓词。如果你想使用一个
集合
只需使它也符合
可散列
。@LeoDabus我喜欢这个解决方案。当你说你有“多个函数”时,其他函数是什么样子的?我怀疑你把这段代码写错了方向。它们是否都有类似于
$0.id==newthing.id
的内容,或者它们是否有其他谓词?所有谓词都是
$0.==新事物。
(即使不仅仅是
id
)?还是这些谓词要复杂得多?泛型代码必须始终从它的调用方式开始,否则您将走上错误的道路。虽然数组的解决方案可能会起作用,但我更喜欢符合equalable的解决方案,因为它很简单,您需要通过仅检查object
id
s是否相等来覆盖
=
equalable
方法。什么不适合所有情况。例如,有两个对象具有相同的
id
s,但具有不同的
name
s。他们应该平等吗?这就是为什么我也希望有专门设计的protocolI同意在这里使用equalable是不正确的,但是这个协议的用途非常单一(特别是对于“可替换”这样的通用名称)。问题中的原始代码在我看来要好得多。如果需要扩展,
where Element==Fruit
将是适当的限制,直到您可以在同一程序中找到足够多的其他用例来提取有意义的协议。@TarasChernyshenko在重新考虑Equalable之后,我同意这不是最好的解决方案。虽然数组的解决方案可能有效,但我更喜欢符合equalable的解决方案,因为它很简单。您需要通过仅检查对象
id
s是否相等来覆盖
=
equalable的
=
方法。什么不适合所有情况。例如,有两个对象具有相同的
id
s,但具有不同的
name
s。他们应该平等吗?这就是为什么我也希望有专门设计的protocolI同意在这里使用equalable是不正确的,但是这个协议的用途非常单一(特别是对于“可替换”这样的通用名称)。问题中的原始代码在我看来要好得多。如果需要扩展,
where Element==Fruit
将是适当的限制,直到您可以在同一程序中找到足够多的其他用例来提取有意义的协议。@TarasChernyshenko在重新考虑Equalable之后,我同意这不是最好的解决方案。您的
TestType的示例实现==<