使用枚举的默认值作为字典键而不显式转换为字符串

时间:2016-03-01 01:13:49

标签: swift dictionary enums key

如果我声明enum这样:

enum Keys {
    case key_one
    case key_two
}

我可以打印它,它会自动转换为String

print(Keys.key_one) // prints "key_one"

如果我然后制作一个将Strings映射到任何地方的字典(但为了简单起见,让我们再次选择Strings),我认为应该可以使用{添加密钥{1}}作为关键,对吗?错。

Keys.key_one

如果我明确地将var myDict = [String : String]() /// error: cannot subscript a value of type '[String : String]' with an index /// of type 'Keys' myDict[Keys.key_one] = "firstKey" 转换为这样的Keys.key_one,我就可以这样做:

String

所以我想这样做,而不必每次都用myDict[String(Keys.key_one)] = "firstKey" print(myDict) // ["key_one": "firstKey"] 包裹enum

我使用String() extension使用Dictionary关键字where尝试实施新Key,尝试了一些事情。功能,但我不能让它工作。这个诀窍是什么?

解决方案更新

我找到了我的解决方案(see below)。

3 个答案:

答案 0 :(得分:7)

Swift 4.2的更新和改进

extension Dictionary {
    subscript(key: APIKeys) -> Value? {
        get {
            guard let key = key.stringValue as? Key else { return nil }
            return self[key]
        }
        set(value) {
            guard let key = key.stringValue as? Key else { return }
            guard let value = value else { self.removeValue(forKey: key); return }
            self.updateValue(value, forKey: key)
        }
    }
}

protocol APIKeys {}
extension APIKeys {
    var stringValue: String {
        return String(describing: self)
    }
}

enum Keys: APIKeys {
    case key_one
    case key_two
}

var myStringDict = [AnyHashable : Any]()
var model1StringDict = [String : Any]()
var model2StringDict = [String : String]()

myStringDict.updateValue("firstValue", forKey: Keys.key_one.stringValue)                    // [key_one: firstValue]
myStringDict[Keys.key_two] = "secondValue"                                                  // [key_two: secondValue, key_one: firstValue]
myStringDict[Keys.key_one] = nil                                                            // [key_two: secondValue]
myStringDict.removeValue(forKey: Keys.key_two.stringValue)                                  // []

model1StringDict.updateValue("firstValue", forKey: Model1Keys.model_1_key_one.stringValue)  // [model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue"                                // [model_1_key_two: secondValue, model_1_key_one: firstValue]
model1StringDict[Model1Keys.model_1_key_one] = nil                                          // [model_1_key_two: secondValue]

model2StringDict.updateValue("firstValue", forKey: Model2Keys.model_2_key_one.stringValue)  // [model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue"                                // [model_2_key_two: secondValue, model_2_key_one: firstValue]
model2StringDict[Model2Keys.model_2_key_one] = nil                                          // [model_2_key_two: secondValue]

我专门更改了3个字典的类型,以显示键入字典的常用方法([AnyHashable : Any][String : Any][String : String]),并显示这适用于每个字典类型。

请务必注意,如果在词典的密钥为updateValue时使用AnyHashable而不是赋值运算符,则需要指定密钥的String.stringValue。否则,存储的密钥的确切类型将不会明确地为String,如果您尝试通过将nil分配给String来删除密钥下的值,则会稍后搞砸那把钥匙。对于专门键入密钥为updateValue的字典,String函数将出现编译时错误,指出密钥需要为extension Dictionary { subscript(key: Keys) -> Value? { get { return self[String(key) as! Key] } set(value) { guard let value = value else { self.removeValueForKey(String(key) as! Key) return } self.updateValue(value, forKey: String(key) as! Key) } } } enum Keys { case key_one case key_two } var myStringDict = [String : String]() /// Adding the first key value through the String() way on purpose myStringDict.updateValue("firstValue", forKey: String(Keys.key_one)) // myStringDict: ["key_one": "firstValue"] // myStringDict[Keys.key_one]!: firstValue myStringDict[Keys.key_two] = "secondValue" // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"] myStringDict[Keys.key_one] = nil // myStringDict: ["key_two": "secondValue"] ,因此您不能搞砸了。

Swift 2.3

我想出了我想要的扩展解决方案。

String

请注意,声明的字典键类型为Keys.key_one,但我只能使用as!,字典扩展中的下标可以处理其余部分。

我可以更好地保护Key转换为Key,但我不确定是否需要,因为我知道我的枚举总是可以转换为有效{{1} } String()演员。

回答改进

更好的是,因为我将它用于API密钥,所以我制作了一个名为APIKeys的空白协议,每个模型都会实现符合Keys协议的自己的APIKeys枚举。字典的下标已更新,以APIKeys作为Key值。

extension Dictionary {

    subscript(key: APIKeys) -> Value? {
        get {
            return self[String(key) as! Key]
        }
        set(value) {
            guard
                let value = value else {
                    self.removeValueForKey(String(key) as! Key)
                    return
            }

            self.updateValue(value, forKey: String(key) as! Key)
        }
    }

}

protocol APIKeys {}

enum Keys: APIKeys {
    case key_one
    case key_two
}

enum Model1Keys: APIKeys {
    case model_1_key_one
    case model_1_key_two
}

enum Model2Keys: APIKeys {
    case model_2_key_one
    case model_2_key_two
}

var myStringDict = [String : String]()
var model1StringDict = [String : String]()
var model2StringDict = [String : String]()

myStringDict.updateValue("firstValue", forKey: String(Keys.key_one))    // myStringDict: ["key_one": "firstValue"]
myStringDict[Keys.key_two] = "secondValue"                              // myStringDict: ["key_one": "firstValue", "key_two": "secondValue"]
myStringDict[Keys.key_one] = nil                                        // myStringDict: ["key_two": "secondValue"]

model1StringDict.updateValue("firstValue", forKey: String(Model1Keys.model_1_key_one))  // model1StringDict: ["model_1_key_one": "firstValue"]
model1StringDict[Model1Keys.model_1_key_two] = "secondValue"                            // model1StringDict: ["model_1_key_one": "firstValue", "model_1_key_two": "secondValue"]
model1StringDict[Model1Keys.model_1_key_one] = nil                                      // model1StringDict: ["model_1_key_two": "secondValue"]

model2StringDict.updateValue("firstValue", forKey: String(Model2Keys.model_2_key_one))  // model2StringDict: ["model_2_key_one": "firstValue"]
model2StringDict[Model2Keys.model_2_key_two] = "secondValue"                            // model2StringDict: ["model_2_key_one": "firstValue", "model_2_key_two": "secondValue"]
model2StringDict[Model2Keys.model_2_key_one] = nil                                      // model2StringDict: ["model_2_key_two": "secondValue"]

答案 1 :(得分:1)

或者你可以这样做:

enum Keys: String {
    case key_one
    case key_two
}

并像这样使用它:

var myDict = [String : String]()

myDict[Keys.key_one.rawValue] = "firstKey"

<强>更新

如果您真的想让它发挥作用,您可以执行以下操作。但这些都是通过强制解包来冒险的。

extension Dictionary where Key: StringLiteralConvertible {
    subscript (key: Keys) -> Value? {
        get {
            return self[key.rawValue as! Key]
        }
        set {
            self[key.rawValue as! Key] = newValue
        }
    }
}

然后你就这样使用它:

myDict[Keys.key_one] = "firstKey"
myDict[Keys.key_one]

答案 2 :(得分:0)

您的密钥是字符串有多重要?如果它不重要,我建议做类似的事情:

enum Keys {
    case key_one
    case key_two
}

var myDict = [Keys : String]()

myDict[Keys.key_one] = "firstKey"
print(myDict) // [Keys.key_one: "firstKey"]