2016-08-04 39 views
4

基於字符串的枚舉我想延長DictionaryString鍵(JSON字典),以允許與一個具有RawValue類型的String任何enum下標。最終目標是多個enums,可用於標記JSON字典。標字典與夫特

enum JSONKey: String { 
    case one, two, three 
} 

enum OtherJSONKey: String { 
    case a, b, c 
} 

if let one = jsonDictionary[.one] { /* ... */ } 
if let b = jsonDictionary[.b] { /* ... */ } 

但我無法弄清楚如何實現這一點。我知道我需要擴展Dictionary,但無法弄清楚通用擴展約束或方法擴展約束。

我的第一個想法是儘量通用約束添加到標方法。但我不認爲下標方法允許泛型。

extension Dictionary { 
    subscript<T: RawRepresentable>(key: T) -> Value? { /* ... */ } 
} 

即使將下標的通用約束工作,我仍然需要一種方法來嵌套我的通用約束。或者將字典限制爲基於字符串的枚舉鍵。爲了把它的代碼是無效的,我想這樣做:

extension Dictionary where Key: RawRepresentable where RawValue == String { 
    subscript(key: Key) -> Value { /* ... */ } 
} 

// or 

extension Dictionary { 
    subscript<T: RawRepresentable where RawValue == String>(key: T) -> Value { /* ... */ } 
} 

正在擴大Dictionary接受基於字符串的枚舉作爲實際上可能標?

我對如何實現這樣的東西的其他想法包括enum繼承和創建一個特定的enums協議,我想用作下標。我知道其中一些做不了,但認爲值得一提的是這個想法。所以,再次把它的代碼是無效的:

enum JSONKey: String {} 
enum NumbersJSONKey: JSONKey { 
    case one, two, three 
} 
enum LettersJSONKey: JSONKey { 
    case a, b, c 
} 

// or 

protocol JSONKeys {} 
enum NumbersJSONKey: JSONKey { 
    case one, two, three 
} 
enum LettersJSONKey: JSONKey { 
    case a, b, c 
} 

// then subscript with 
if let one = json[.one] { /* ... */ } 

更新:

我打這個多一些,並得到一點點接近。下面的擴展名編譯,但如果我真的嘗試使用它,會給我一個「下標不明確」的錯誤。

extension Collection where Iterator.Element == (key: String, value: AnyObject) { 

    // Compiles but can't be used because of ambiguous subscript. 
    subscript(key: CustomStringConvertible) -> AnyObject? { 
     guard let i = index(where: { $0.key == key.description }) else { return nil } 
     return self[i].value 
    } 

} 

@ titaniumdecoy的回答工作所以這將是公認的答案,除非有人能想出更好的東西。

+0

在'[String:String]'字典中,您將無法使用'.one'對其進行下標。您必須使用'JSONKey.one'來執行此操作(除了您要求的擴展外)。 – WMios

+0

這仍然很好。這允許使用JSON鍵的名稱空間而不是鍵入'json [「someKey」]或保留一堆'String'常量。 – keithbhunter

+0

想通了。看看我的答案。 – WMios

回答

6

據我瞭解,你要在任何字典的擴展與字符串鍵允許使用帶有枚舉下標字符串作爲其RawValue類型。如果是這樣,下面應該爲你工作:

enum JSONKey: String { 
    case one, two, three 
} 

class JSONObject { } 

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

var jsonDict: [String: AnyObject] = [:]  

jsonDict[JSONKey.one] = JSONObject() 
jsonDict["two"] = JSONObject() 

print(jsonDict["one"]!) 
print(jsonDict[JSONKey.two]!) 

如果你想延長這種爲任何工作枚舉與字符串作爲其RawValue類型,則需要仿製藥。隨着斯威夫特不支持通用標(見SR-115),get/set方法或屬性將需要:

enum AnotherEnum: String { 
    case anotherCase 
} 

extension Dictionary where Key: StringLiteralConvertible { 
    func getValue<T: RawRepresentable where T.RawValue == String>(forKey key: T) -> Value? { 
     return self[key.rawValue as! Key] 
    } 
    mutating func setValue<T: RawRepresentable where T.RawValue == String>(value: Value, forKey key: T) { 
     self[key.rawValue as! Key] = value 
    } 
} 

jsonDict.setValue(JSONObject(), forKey: AnotherEnum.anotherCase) 
print(jsonDict.getValue(forKey: AnotherEnum.anotherCase)!) 
+1

好的答案。 Fyi,在二傳手中,你不需要傳遞它的價值。 'newValue'關鍵字涵蓋了這一點(如我的答案所示)。 – WMios

+0

謝謝。改變。 – titaniumdecoy

+0

這應該可能被標記爲答案。 – WMios

3

所以這個工作對我來說:

enum JSONKey: String { 
    case one 
    case two 
    case three 
} 

extension Dictionary { 
    subscript(key: JSONKey) -> Value { 
     get { 
      let k = key.rawValue as! Key 
      return self[k]! 
     } 
     set { 
      let k = key.rawValue as! Key 
      self[k] = newValue 
     } 
    } 

} 

var jsonDictionary = [JSONKey.one.rawValue : "hello", JSONKey.two.rawValue : "hi there", JSONKey.three.rawValue : "foobar", "fourth value" : 4] 

let one = jsonDictionary[.one] 
let two = jsonDictionary[.two] 
var three = jsonDictionary[.three] 
let four = jsonDictionary["fourth value"] 

jsonDictionary[.three] = 5 
three = jsonDictionary[.three] 

print("One: \(one), Two: \(two), Three: \(three), Four: \(four!)") 

而且它打印:

"One: hello, Two: hi there, Three: 5, Four: 4\n" 
2

與SWIFT 4對Generic Subscripts支持你現在可以這樣做:

extension Dictionary where Key: ExpressibleByStringLiteral { 
    subscript<Index: RawRepresentable>(index: Index) -> Value? where Index.RawValue == String { 
     get { 
      return self[index.rawValue as! Key] 
     } 

     set { 
      self[index.rawValue as! Key] = newValue 
     } 
    } 
} 

哪讓您使用任何枚舉它有字符串,因爲它的RawValue類型:

let value = jsonDict[JSONKey.one]

這將現在適用於任何字符串枚舉,而不僅僅是JSONKey

+0

這看起來像一個很好的解決方案,但我似乎錯過了我的實現中的一步,但是。當我使用上面的字典擴展名時,然後聲明一個字符串鍵的枚舉類似於: 枚舉鍵:字符串{case} daily =「daily」 case current =「currently」 case temperature =「temperature」 case summary =「summary 「 } 然後嘗試在代碼中使用它: let temperature = json [Keys.currently] [Keys.temperature] .double 我得到一個錯誤標記:」不能下標JSON類型的值'與'WeatherDetail.Keys'類型的索引「。 – Gallaugher

+0

@Gallaugher'不能下標'JSON'類型的值。問題在於'JSON'。下標是爲「Dictionary」類型定義的。如果你想使用這個解決方案的例子,請看這裏:https://github.com/mluisbrown/iCalendar/blob/master/Sources/iCalendar/Event.swift – mluisbrown