简体   繁体   English

下标:使用String枚举访问我的字典值

[英]Subscript: access my dictionary values with a String enumeration

I want to do something like that: access my dictionary values with a String enumeration. 我想做这样的事情:使用String枚举访问我的字典值。 I am trying to overload the subscript of the dictionary but without success. 我试图重载字典的下标,但没有成功。

Accessing the dictionary: 访问字典:

let district = address[JsonKeys.district]

where JsonKeys is: JsonKeys在哪里:

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): PS:不想这样做(这可以纠正错误,但是这种方式无法读取代码):

let district = address[JsonKeys.district.rawValue]

The dictionary is a Json parsed dictionary given to me by AlamoFire. 该词典是AlamoFire给我的Json解析词典。 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. 在Swift中解除类型非常简单。 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 . 请记住,在将约束作为Key: StringLiteralConvertible ,该扩展适用于其Key符合StringLiteralConvertible任何Dictionary。 (You know many types other than String conform to StringLiteralConvertible .) (您知道String以外的许多其他类型都符合StringLiteralConvertible 。)

To call subscript self[] , you need to pass a value of type Key . 要调用下标self[] ,您需要传递Key类型的值。 index.rawValue is String , which may not always be a Key in the extension. index.rawValueString ,它不一定总是扩展中的Key

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 . 相反,每个枚举都需要符合UsesRawValue Now we can use it like this. 现在我们可以像这样使用它。

ajson[JsonKeys.key1]
ajson[JsonKeys.key1] = "name"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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