It would be nice to have mapKeys<T>
and compactMapKeys<T>
functions for dictionaries, just as we now have mapValues<T>
and compactMapValues<T>
in Swift 4.0.
The motivation behind this is that there are concrete cases where you may need to modify the keys of a dictionary, leaving values intact, and to do this today requires an unnecessarily complex reduce
function.
An example of this is working with NSAttributedString
in UIKit
.
Say that we have
[NSAttributedStringKey: Any]dictionaries encapsulating styles defined in a global constants file that we access regularly to generate attributed strings with the
NSAttributedString(string: String, attributes: [NSAttributedStringKey: Any]?) initializer.
When we want to use these dictionaries to, say, set the defaultTextAttributes
on a UITextField
instance, we run into the problem that UITextField.defaultTextAttributes
expects [String: Any]
as the dictionary type.
To adapt this attributes dictionary today, a possible approach is to use the reduce function:
var attributes: [NSAttributedStringKey: Any] = [
.font: UIFont.systemFont(ofSize: 15),
.foregroundColor: UIColor.white
]
attributes.reduce(into: [String: Any](), { result, x in
result[x.key.rawValue] = x.value
})
Granted in this example this is a bit trivial but you can see how the complexity can dramatically increase if we want to transform the key in more elaborate ways.
A simple extension to Dictionary to introduce map
-type functions could be as simple as:
extension Dictionary {
func compactMapKeys<T>(_ transform: ((Key) throws -> T?)) rethrows -> Dictionary<T, Value> {
return try self.reduce(into: [T: Value](), { (result, x) in
if let key = try transform(x.key) {
result[key] = x.value
}
})
}
}
yet this simplifies our function to:
var attributes: [NSAttributedStringKey: Any] = [
.font: UIFont.systemFont(ofSize: 15),
.foregroundColor: UIColor.white
]
attributes.compactMapKeys { $0.rawValue }
Thoughts as to whether or not this is worthwhile addition to the standard library is much appreciated!