2015-07-11 79 views
1

我一直在研究一個遞歸函數來提取表示爲NSDictionary的JSON數據中的字符串值。該功能可讓您做到這一點:如何利用Swift特性來重構這個遞歸函數?

if let value = extractFromNestedDictionary(["fee" : ["fi" : ["fo" : "fum"]]], withKeys: ["fee", "fi", "fo"]) { 
    println("\(value) is the value after traversing fee-fi-fo"); 
} 

,函數執行如下:

// Recursively retrieves the nested dictionaries for each key in `keys`, 
// until the value for the last key is retrieved, which is returned as a String? 
func extractFromNestedDictionary(dictionary: NSDictionary, withKeys keys: [String]) -> String? { 
    if keys.isEmpty { return nil } 
    let head = keys[0] 
    if let result: AnyObject = dictionary[head] { 
     if keys.count == 1 { 
      return result as? String 
     } else { 
      let tail: [String] = Array(keys[1..<keys.count]) 
      if let result = result as? NSDictionary { 
       return extractFromNestedDictionary(result, withKeys: tail) 
      } else { 
       return nil 
      } 
     } 
    } else { 
     return nil 
    } 
} 

是否有雨燕1.2/2.x中,可以結合有關可選擇一些語法特點:

  • 使該功能更加簡潔
  • 少用if築巢
+0

這個問題看起來像一個很好的候選人http://codereview.stackexchange.com。 –

+0

感謝@MartinR,我不知道那個堆棧交換筒倉。 – ybakos

回答

3

相反遞歸的,你可以使用reducekeys陣列 通過字典來遍歷:

func extractFromNestedDictionary(dictionary: NSDictionary, withKeys keys: [String]) -> String? { 

    return reduce(keys, dictionary as AnyObject?) { 
     ($0 as? NSDictionary)?[$1] 
    } as? String 
} 

內關閉,$0是當前級別上的(可選)對象和當前鍵的$1 。如果$0是字典而且具有當前鍵值 和nil,則封閉將返回下一級別的對象 。 reduce()的返回值是 上一級的對象或nil

+0

哦,我嘗試了很長時間來把它寫成帶有'reduce'的單行程。感謝您向我展示燈光! – ybakos

1

我本來不想只是使它沒有你想第一次,但我做到了,反正因爲我愛雨燕有樂在其中:

func extractFromNestedDictionary(dictionary: [NSObject : AnyObject], var withKeys keys: [String]) -> String? { 
    if let head = keys.first, result = dictionary[head] { 
     if keys.count == 1 { 
      return result as? String 
     } else if let result = result as? [NSObject : AnyObject] { 
      keys.removeAtIndex(0) 
      return extractFromNestedDictionary(result, withKeys: keys) 
     } 
    } 
    return nil 
} 


extractFromNestedDictionary(["A" : ["B" : ["C" : "D"]]], withKeys: ["A", "B", "C"]) 

的幾個注意事項:

  • 儘量避免NSDictionary和使用[NSObject : AnyObject]相反,它可以橋接的NSDictionary反正並且更加SWIFTY
  • 當問一個問題,儘量使一個更好的例子比你那裏,從你的例子我無法知道你想要做什麼。
+1

別擔心,在看別人的有趣貢獻之前,我一直在用自己的黑客攻擊。很高興你無法抗拒。 :) – ybakos

+0

感謝您的提示,我已經更新了問題中的方法調用。 – ybakos

+1

只有當你的嵌套字典對每個嵌套字典只有1個項目時,這隻會起作用,因爲你只能看到頭部 – Knight0fDragon

0

這裏是最好的,我想出了...

func extractFromNestedDictionary(dictionary: [NSObject : AnyObject], withKeys keys: [String]) -> String? { 
    if let head = keys.first, 
     result = dictionary[head] as? String 
     where keys.count == 1 { 
      return result 
    } else if let head = keys.first, 
     result = dictionary[head] as? [NSObject : AnyObject] { 
      return extractFromNestedDictionary(result, withKeys: Array(keys[1..<keys.count])) 
    } 
    return nil 
} 
1

我知道這不是嚴格回答問題。但是,你可以只使用valueForKeypath

let fum = dict.valueForKeyPath("fee.fi.fo") 
+0

在很多情況下,這可能是一個很好的解決方案。但請注意,如果這些鍵包含特殊的鍵值編碼(如'.'或'@'),則它不起作用(甚至崩潰)。 –

+0

@mxcl,謝謝你通過我的功能切片,發佈一個簡單的選擇。 – ybakos