DictionaryKeyPath.swift 1.8 KB

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