一尘不染

Swiftier Swift,用于“添加到数组,如果不存在则创建…”

swift

我注意到Swift中的一个常见模式是

var x:[String:[Thing]] = [:]

因此,当您想“将一项添加到数组之一”时,您不能只

x[which].append(t)

你必须

if x.index(forKey: which) == nil {
    x[which] = []
    }
x[which]!.append(s!)

真的,有没有更快捷的方式来表达类似

  x[index?!?!].append??(s?!)
  • 尽管这是关于样式的 问题 ,但是由于 Swift 具有复制特性,因此 在Swift中触摸数组时性能似乎是至关重要的问题

(请注意,显然您可以为此使用扩展名;这是关于Swiftiness的问题。)


阅读 235

收藏
2020-07-07

共1个答案

一尘不染

Swift 4更新:

从Swift
4开始,字典有一种subscript(_:default:)方法,因此

dict[key, default: []].append(newElement)

追加到已经存在的数组或空数组。例:

var dict: [String: [Int]] = [:]
print(dict["foo"]) // nil

dict["foo", default: []].append(1)
print(dict["foo"]) // Optional([1])

dict["foo", default: []].append(2)
print(dict["foo"]) // Optional([1, 2])

Swift 4.1 (当前处于beta版)开始,这也 _很快 比较Hamish的评论。


除了编写的内容外,您还可以使用nil-coalescing运算符

dict[key] = (dict[key] ?? []) + [elem]

或可选链接(nil如果 无法 执行附加操作,则返回):

if dict[key]?.append(elem) == nil {
     dict[key] = [elem]
}

正如SE-0154在“提供字典键和值的自定义集合”中以及
@Hamish在注释中提到的那样,这两种方法均会复制数组。

使用SE-0154的实现,您将能够在不创建副本的情况下更改字典值:

if let i = dict.index(forKey: key) {
    dict.values[i].append(elem)
} else {
    dict[key] = [key]
}

目前,最有效的解决方案由Rob Napier在Swift中的Dictionary中使用MutableArray提供,因为值执行速度非常慢?如何优化或正确构建

var array = dict.removeValue(forKey: key) ?? []
array.append(elem)
dict[key] = array

一个简单的基准确认“ Rob的方法”是最快的:

let numKeys = 1000
let numElements = 1000

do {
    var dict: [Int: [Int]] = [:]

    let start = Date()
    for key in 1...numKeys {
        for elem in 1...numElements {
            if dict.index(forKey: key) == nil {
                dict[key] = []
            }
            dict[key]!.append(elem)

        }
    }
    let end = Date()
    print("Your method:", end.timeIntervalSince(start))
}

do {
    var dict: [Int: [Int]] = [:]

    let start = Date()
    for key in 1...numKeys {
        for elem in 1...numElements {
            dict[key] = (dict[key] ?? []) + [elem]
        }
    }
    let end = Date()
    print("Nil coalescing:", end.timeIntervalSince(start))
}


do {
    var dict: [Int: [Int]] = [:]

    let start = Date()
    for key in 1...numKeys {
        for elem in 1...numElements {
            if dict[key]?.append(elem) == nil {
                dict[key] = [elem]
            }
        }
    }
    let end = Date()
    print("Optional chaining", end.timeIntervalSince(start))
}

do {
    var dict: [Int: [Int]] = [:]

    let start = Date()
    for key in 1...numKeys {
        for elem in 1...numElements {
            var array = dict.removeValue(forKey: key) ?? []
            array.append(elem)
            dict[key] = array
        }
    }
    let end = Date()
    print("Remove and add:", end.timeIntervalSince(start))
}

1000键/ 1000元素的结果(在1.2 GHz Intel Core m5 MacBook上):

您的方法:0.470084965229034
零合并:0.460215032100677
可选链接0.397282958030701
删除并添加:0.160293996334076

对于1000个键/ 10,000个元素:

您的方法:14.6810429692268
零合并:15.1537700295448
可选链接14.4717089533806
删除并添加:1.54668599367142
2020-07-07