Subscript: access my dictionary values with a String enumeration

前端 未结 3 606
隐瞒了意图╮
隐瞒了意图╮ 2021-01-03 16:14

I want to do something like that: access my dictionary values with a String enumeration. I am trying to overload the subscript of the dictionary but without success.

相关标签:
3条回答
  • 2021-01-03 16:33

    The simplest approach is to just lift the dictionary into more context. The context in this case is "it only has keys from this enum." Lifting a type in Swift is very straightforward. Just wrap it in a struct.

    // This could be a nested type inside JSONObject if you wanted.
    enum JSONKeys: String {
        case district
    }
    
    // Here's my JSONObject. It's much more type-safe than the dictionary,
    // and it's trivial to add methods to it.
    struct JSONObject {
        let json: [String: AnyObject]
        init(_ json: [String: AnyObject]) {
            self.json = json
        }
    
        // You of course could make this generic if you wanted so that it
        // didn't have to be exactly JSONKeys. And of course you could add
        // a setter.
        subscript(key: JSONKeys) -> AnyObject? {
            return json[key.rawValue]
        }
    }
    
    let address: [String: AnyObject] = ["district": "Bob"]
    
    // Now it's easy to lift our dictionary into a "JSONObject"
    let json = JSONObject(address)
    
    // And you don't even need to include the type. Just the key.
    let district = json[.district]
    
    0 讨论(0)
  • 2021-01-03 16:36

    I know that this is an old question, but I'd thought I'd add an implementation that is easier to extend, reuse, and more lightweight

    public protocol UsesRawValue {
        var rawValue: String { get }
    }
    
    extension JsonKeys: UsesRawValue {}
    
    extension Dictionary where Key: ExpressibleByStringLiteral {
        public subscript(key: UsesRawValue) -> Value? {
            get { return self[key.rawValue as! Key] }
            set { self[key.rawValue as! Key] = newValue }
        }
    }
    

    Based on this blog post

    This approach only requires us to extend our dictionary once, rather than for each enum. Instead, each enum needs to conform to UsesRawValue. Now we can use it like this.

    ajson[JsonKeys.key1]
    ajson[JsonKeys.key1] = "name"
    
    0 讨论(0)
  • 2021-01-03 16:42

    Try this:

    extension Dictionary where Key: StringLiteralConvertible {
        subscript(index: JsonKeys) -> Value {
            get {
                return self[index.rawValue as! Key]!
            }
        }
    }
    

    Remember, with having constraint as Key: StringLiteralConvertible, the extension works for any Dictionaries with its Key conforming to StringLiteralConvertible. (You know many types other than String conform to StringLiteralConvertible.)

    To call subscript self[], you need to pass a value of type Key. index.rawValue is String, which may not always be a Key in the extension.

    So, the extension I have shown would work for some Dictionaries, would cause runtime crash for some other Dictionaries.


    A little bit more type-safe way:

    protocol MyJsonKeysConvertible {
        init(jsonKeys: JsonKeys)
    }
    extension String: MyJsonKeysConvertible {
        init(jsonKeys: JsonKeys) {self = jsonKeys.rawValue}
    }
    extension Dictionary where Key: MyJsonKeysConvertible {
        subscript(index: JsonKeys) -> Value {
            get {
                return self[Key(jsonKeys: index)]!
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题