Swiftier Swift for 'add to array, or create if not there…'

前端 未结 1 1627
醉梦人生
醉梦人生 2020-12-17 20:24

I\'ve noticed a common pattern in Swift is

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

so, when you want to \"add an item to one of the arrays\", you can

相关标签:
1条回答
  • 2020-12-17 21:06

    Swift 4 update:

    As of Swift 4, dictionaries have a subscript(_:default:) method, so that

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

    appends to the already present array, or to an empty array. Example:

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

    As of Swift 4.1 (currently in beta) this is also fast, compare Hamish's comment here.


    Previous answer for Swift <= 3: There is – as far as I know – no way to "create or update" a dictionary value with a single subscript call.

    In addition to what you wrote, you can use the nil-coalescing operator

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

    or optional chaining (which returns nil if the append operation could not be performed):

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

    As mentioned in SE-0154 Provide Custom Collections for Dictionary Keys and Values and also by @Hamish in the comments, both methods make a copy of the array.

    With the implementation of SE-0154 you will be able to mutate a dictionary value without making a copy:

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

    At present, the most efficient solution is given by Rob Napier in Dictionary in Swift with Mutable Array as value is performing very slow? How to optimize or construct properly?:

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

    A simple benchmark confirms that "Rob's method" is the fastest:

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

    Results (on a 1.2 GHz Intel Core m5 MacBook) for 1000 keys/1000 elements:

    Your method:      0.470084965229034
    Nil coalescing:   0.460215032100677
    Optional chaining 0.397282958030701
    Remove and add:   0.160293996334076
    

    And for 1000 keys/10,000 elements:

    Your method:      14.6810429692268
    Nil coalescing:   15.1537700295448
    Optional chaining 14.4717089533806
    Remove and add:   1.54668599367142
    
    0 讨论(0)
提交回复
热议问题