Daniel Daniel - 6 months ago 31
Swift Question

Dictionary of dictionaries in Swift

I'd like to build a dictionary of dictionaries. In Swift how do I declare a dictionary with a key of a String and the value of a dictionary of this same type? I need to be able to have infinite nests. (Kind of like building a tree using nodes. Except it's not a tree, it's a dictionary.)

I tried using AnyObject, but get a conversion error:

var node1: Dictionary<String, AnyObject?> = ["foo" : nil]
var node2: Dictionary<String, AnyObject?> = ["bar" : node1] // ERROR: Cannot convert value of type 'Dictionary<String, AnyObject?>' (aka 'Dictionary<String, Optional<AnyObject>>') to expected dictionary value type 'Optional<AnyObject>'


Is there a type-safe way of doing this (i.e., not using AnyObject?)

Answer

You can achieve something like this with a nice API and type safety in swift by using a struct and an enumeration.

enum RecursiveDictValue<KeyType: Hashable, ValueType> {
    case Value(ValueType)
    case Dict(RecursiveDict<KeyType, ValueType>)
}

struct RecursiveDict<KeyType: Hashable, ValueType> {
    typealias OwnType = RecursiveDict<KeyType, ValueType>

    private var dict: [KeyType: RecursiveDictValue<KeyType, ValueType>]

    init() {
        dict = [:]
    }

    init(dict: [KeyType: RecursiveDictValue<KeyType, ValueType>]) {
        self.dict = dict
    }

    // this ensures that we can safely chain subscripts
    subscript(key: KeyType) -> OwnType {
        get {
            switch dict[key] {
            case let .Dict(dict)?:
                return dict
            default:
                return RecursiveDict<KeyType, ValueType>()
            }
        }

        set(newValue) {
            dict[key] = .Dict(newValue)
        }
    }

    subscript(key: KeyType) -> ValueType? {
        get {
            switch dict[key] {
            case let .Value(value)?:
                return value
            default:
                return nil
            }
        }

        set(newValue) {
            if let newValue = newValue {
                dict[key] = RecursiveDictValue<KeyType, ValueType>.Value(newValue)
            } else {
                dict[key] = nil
            }
        }
    }
}

This works quite nicely (note that you need to help swift with the types though):

var dict = RecursiveDict<String, Int>(dict: ["value":.Value(1),
                                             "dict":.Dict(RecursiveDict<String, Int>(dict: ["nestedValue": .Value(2)]))])

if let value: Int = dict["value"] {
    print(value) // prints 1
}

if let value: Int = dict["dict"]["nestedValue"] {
    print(value) // prints 2
}

It also fails as intended when you do stuff that can't work.

if let value: Int = dict["dict"] {
    print(value) // is not executed
}

if let value: Int = dict["dict"]["nestedDict"]["nestedValue"] {
    print(value) // is not executed
}

And you can even set values in nested dictionaries that haven't been created yet!:

dict["dict"]["nestedDict2"]["nestedValue"] = 3


if let value: Int = dict["dict"]["nestedDict2"]["nestedValue"] {
    print(value) // prints 3
}
Comments