swift: modifying arrays inside dictionaries

后端 未结 5 933
我寻月下人不归
我寻月下人不归 2020-11-30 11:16

How can I easily add elements to an array inside a dictionary? It\'s always complaining with could not find member \'append\' or could not find an overloa

相关标签:
5条回答
  • 2020-11-30 11:21

    The accepted answer bypasses the following much simpler possibility, which also works for older Swift versions:

    var dict = Dictionary<String, Array<Int>>()
    dict["key"] = [1, 2, 3]
    
    print(dict)
    
    dict["key", default: [Int]()].append(4)
    
    print(dict)
    

    This will print:

    ["key": [1, 2, 3]]
    ["key": [1, 2, 3, 4]]
    

    And this:

    var dict = Dictionary<String, Array<Int>>()
    dict["key", default: [Int]()].append(4)
    print(dict)
    

    will print:

    ["key": [4]]
    
    0 讨论(0)
  • 2020-11-30 11:32

    As a simple workaround you can use a NSMutableArray:

    import Foundation
    
    var dict = Dictionary<String, NSMutableArray>()
    dict["key"] = [1, 2, 3] as NSMutableArray
    dict["key"]!.addObject(4)
    

    I am using effectively such simple solution in my project:

    https://github.com/gui-dos/Guigna/blob/5c02f7e70c8ee3b2265f6916c6cbbe5cd3963fb5/Guigna-Swift/Guigna/GuignaAppDelegate.swift#L1150-L1157

    0 讨论(0)
  • 2020-11-30 11:36

    Swift beta 5 has added this functionality, and you've nailed the new method in a couple of your attempts. The unwrapping operators ! and ? now pass through the value to either operators or method calls. That is to say, you can add to that array in any of these ways:

    dict["key"]! += [4]
    dict["key"]!.append(4)
    dict["key"]?.append(4)
    

    As always, be careful about which operator you use -- force unwrapping a value that isn't in your dictionary will give you a runtime error:

    dict["no-key"]! += [5]        // CRASH!
    

    Whereas using optional chaining will fail silently:

    dict["no-key"]?.append(5)     // Did it work? Swift won't tell you...
    

    Ideally you'd be able to use the new null coalescing operator ?? to address this second case, but right now that's not working.


    Answer from pre-Swift beta 5:

    It's a quirk of Swift that it's not possible to do what you're trying to do. The issue is that the value of any Optional variable is in fact a constant -- even when forcibly unwrapping. If we just define an Optional array, here's what we can and can't do:

    var arr: Array<Int>? = [1, 2, 3]
    arr[0] = 5
    // doesn't work: you can't subscript an optional variable
    arr![0] = 5
    // doesn't work: constant arrays don't allow changing contents
    arr += 4
    // doesn't work: you can't append to an optional variable
    arr! += 4
    arr!.append(4)
    // these don't work: constant arrays can't have their length changed
    

    The reason you're having trouble with the dictionary is that subscripting a dictionary returns an Optional value, since there's no guarantee that the dictionary will have that key. Therefore, an array in a dictionary has the same behavior as the Optional array, above:

    var dict = Dictionary<String, Array<Int>>()
    dict["key"] = [1, 2, 3]
    dict["key"][0] = 5         // doesn't work
    dict["key"]![0] = 5        // doesn't work
    dict["key"] += 4           // uh uh
    dict["key"]! += 4          // still no
    dict["key"]!.append(4)     // nope
    

    If you need to change something in an array in the dictionary you'll need to get a copy of the array, change it, and reassign, like this:

    if var arr = dict["key"] {
        arr.append(4)
        dict["key"] = arr
    }
    

    ETA: Same technique works in Swift beta 3, though constant arrays no longer allow changes to contents.

    0 讨论(0)
  • 2020-11-30 11:41

    Here is what I was telling Nate Cook, in the comments for his quality answer. This is what I consider "easily [adding] elements to an array inside a dictionary":

    dict["key"] = dict["key"]! + 4
    dict["key"] = dict["key"] ? dict["key"]! + 4 : [4]
    

    For now, we need to define the + operator ourselves.

    @infix func +<T>(array: T[], element: T) -> T[] {
        var copy = array
        copy += element
        return copy
    }
    

    I think this version removes too much safety; maybe define it with a compound operator?

    @infix func +<T>(array: T[]?, element: T) -> T[] {
        return array ? array! + element : [element]
    }
    
    dict["key"] = dict["key"] + 4
    

    Finally, this is the cleanest I can get it, but I'm confused about how array values/references work in this example.

    @assignment func +=<T>(inout array: T[]?, element: T) {
        array = array + element
    }
    
    dict["key"] += 5
    
    0 讨论(0)
  • 2020-11-30 11:42

    Use 'for in', for getting values from dictionary's inside array. Here's an example to help you

    var a = ["x":["a","b","c"], "y":["d"]]
    
    for b in a {
        print(b)
    }
    

    Output:

    ("x", ["d"])
    ("y", ["a", "b", "c"])
    
    0 讨论(0)
提交回复
热议问题