DictionaryKeyPath.swift 1.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. // For details, see
  2. // http://stackoverflow.com/questions/40261857/remove-nested-key-from-dictionary
  3. import Foundation
  4. extension Dictionary {
  5. subscript(keyPath keyPath: String) -> Any? {
  6. get {
  7. guard let keyPath = Dictionary.keyPathKeys(forKeyPath: keyPath)
  8. else { return nil }
  9. return getValue(forKeyPath: keyPath)
  10. }
  11. set {
  12. guard let keyPath = Dictionary.keyPathKeys(forKeyPath: keyPath),
  13. let newValue = newValue else { return }
  14. self.setValue(newValue, forKeyPath: keyPath)
  15. }
  16. }
  17. static private func keyPathKeys(forKeyPath: String) -> [Key]? {
  18. let keys = forKeyPath.components(separatedBy: ".")
  19. .reversed().flatMap({ $0 as? Key })
  20. return keys.isEmpty ? nil : keys
  21. }
  22. // recursively (attempt to) access queried subdictionaries
  23. // (keyPath will never be empty here; the explicit unwrapping is safe)
  24. private func getValue(forKeyPath keyPath: [Key]) -> Any? {
  25. guard let value = self[keyPath.last!] else { return nil }
  26. return keyPath.count == 1 ? value : (value as? [Key: Any])
  27. .flatMap { $0.getValue(forKeyPath: Array(keyPath.dropLast())) }
  28. }
  29. // recursively (attempt to) access the queried subdictionaries to
  30. // finally replace the "inner value", given that the key path is valid
  31. private mutating func setValue(_ value: Any, forKeyPath keyPath: [Key]) {
  32. guard self[keyPath.last!] != nil else { return }
  33. if keyPath.count == 1 {
  34. (value as? Value).map { self[keyPath.last!] = $0 }
  35. }
  36. else if var subDict = self[keyPath.last!] as? [Key: Value] {
  37. subDict.setValue(value, forKeyPath: Array(keyPath.dropLast()))
  38. (subDict as? Value).map { self[keyPath.last!] = $0 }
  39. }
  40. }
  41. }