简体   繁体   中英

Subscript: access my dictionary values with a String enumeration

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.

Accessing the dictionary:

let district = address[JsonKeys.district]

where JsonKeys is:

enum JsonKeys: String {
    case key1
    case key2
    case key...
}

and my subscript overload is as follow:

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

I get the following message:

**Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'**

Where am I wrong?

PS: don't want to do this (this would correct the error, but the code is unreadable this way):

let district = address[JsonKeys.district.rawValue]

The dictionary is a Json parsed dictionary given to me by AlamoFire. I am pretty sure I can't change its type.

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]

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

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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM