Skip to content

Instantly share code, notes, and snippets.

@ollieatkinson
Last active July 31, 2022 19:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ollieatkinson/e8957b1f42cb1eaf0d8364ec654bc034 to your computer and use it in GitHub Desktop.
Save ollieatkinson/e8957b1f42cb1eaf0d8364ec654bc034 to your computer and use it in GitHub Desktop.
DeepMerge and DeepMerge for Dictionary's in Swift
public struct DeepMapOptions: OptionSet {
public let rawValue: UInt
public init(rawValue: UInt) { self.rawValue = rawValue }
public static let mappingOverArrays = DeepMapOptions(rawValue: 1 << 0)
public static let all: DeepMapOptions = [.mappingOverArrays]
}
extension Dictionary where Value == Any {
public func deepMerging(
_ other: Self,
uniquingKeysWith policy: (Value, Value) -> Value
) -> Self {
merging(other) { old, new in
if let old = old as? Self, let new = new as? Self {
return old.deepMerging(new, uniquingKeysWith: policy)
} else {
return policy(old, new)
}
}
}
public func deepMap(
_ options: DeepMapOptions = [.mappingOverArrays],
_ transform: (Key, Value) throws -> (Key, Value)
) rethrows -> Self {
try reduce(into: [Key: Value](minimumCapacity: count)) { dictionary, next in
let (key, value) = try transform(next.key, next.value)
switch value {
case let o as Self:
dictionary[key] = try o.deepMap(options, transform)
case let o as [Self] where options.contains(.mappingOverArrays):
dictionary[key] = try o.map { try $0.deepMap(options, transform) }
default:
dictionary[key] = value
}
}
}
public func deepMapAndMerge(
_ options: DeepMapOptions = [.mappingOverArrays],
_ transform: (Key, Value) throws -> (Key, Value),
uniquingKeysWith policy: (Value, Value) -> Value = { $1 }
) rethrows -> Self {
try reduce(into: [Key: Value](minimumCapacity: count)) { dictionary, next in
let (key, value) = try transform(next.key, next.value)
switch value {
case let o as Self:
let mapped = try o.deepMapAndMerge(options, transform, uniquingKeysWith: policy)
if let x = dictionary[key] as? Self {
dictionary[key] = x.deepMerging(mapped, uniquingKeysWith: policy)
} else {
dictionary[key] = mapped
}
case let o as [Self] where options.contains(.mappingOverArrays):
dictionary[key] = try o.map {
try $0.deepMapAndMerge(options, transform, uniquingKeysWith: policy)
}
default:
dictionary[key] = value
}
}
}
}
@ollieatkinson
Copy link
Author

let a: [String: Any] = [
    "somePrefix_A": "value",
    "somePrefix_B": "value",
    "somePrefix_C": "value",
    "anotherPrefix_A": "value",
    "anotherPrefix_B": "value",
    "anotherPrefix_C": "value",
    "nested_A": [
        [
            "prefix_A": "value1",
            "prefix_B": "value2"
        ],
        [
            "prefix_C": "value3",
            "prefix_D": "value4"
        ]
    ],
    "nested_B": [
        "some_A": [
            "prefix_A": "value",
            "prefix_B": "value"
        ],
        "some_B": [
            "prefix_A": "value",
            "prefix_B": "value"
        ]
    ]
]

let mapped = a.deepMapAndMerge { key, value in
    if key.contains("_") {
        let parts = key.split(separator: "_").map(String.init)
        return (parts[0], [parts[1]: value])
    } else {
        return (key, value)
    }
}

print(mapped.debugDescription)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment