I use the KeyValuePairs
type a lot. Most recently I wanted to use it when constructing some URLComponents
and making sure that the query parameters were in the correct order and properly escaped:
extension URLComponents {
mutating func setQueryItems(_ items: KeyValuePairs<String, String?>) {
queryItems = items.map { key, value in
return URLQueryItem(name: key,
value: value?.addingPercentEncoding(
withAllowedCharacters: .urlQueryAllowed))
}
}
}
Now, this works fine, but it got me to thinking that it might be cleaner to express the percent encoding as its own step. I wrote a quick extension to KeyValuePairs
to map over the values and return an array of tuples:
extension KeyValuePairs {
/// Returns an array containing the results of mapping the given closure
/// over the sequence’s values.
///
/// - Parameter transform: A mapping closure. `transform` accepts an value
/// of this sequence as its parameter and returns a
/// transformed value of the same or of a different
/// type.
/// - Returns: An array containing the transformed elements of this
/// sequence.
func mapValues<T>(
_ transform: (Value) throws -> T
) rethrows -> [(key: Key, value: T)] {
return try map { key, value in return (key, try transform(value)) }
}
}
This allows me to split the original URLComponents
extension into two steps for readability:
extension URLComponents {
mutating func setQueryItems(_ items: KeyValuePairs<String, String?>) {
queryItems = items.mapValues {
$0?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
}
.map(URLQueryItem.init(name:value:))
}
}
What I would really like to do is to write these methods to return KeyValuePairs
instead of Array
s:
func map<T, U>(_ transform: (Key, Value) throws -> (key: T, value: U)) rethrows -> KeyValuePairs<T, U>
func mapKeys<T>(_ transform: (Key) throws -> T) rethrows -> KeyValuePairs<T, Value>
func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> KeyValuePairs<Key, T>
(I would also probably write the corresponding compactMap
versions.)
Unfortunately, with what’s exposed in the standard library, we can’t do that today. I would at the very least need an append(_:)
method to add a new element to a KeyValuePairs
instance. I know the KeyValuePairs
type is intended to be a very lightweight ordered collection of key-value pairs, but is there any interest in adding some or all of this functionality to the standard library version?