That's what I'm doing now as a workaround; mapping from x
to (x + 1) % m
in my closure. But this unnecessarily creates a brand new dictionary, which I then copy on top of the current one. Even if @Karl's speculation comes true and there's some slick optimization to get rid of the extra work, it would still be (looking) logically wrong.
Collections that can't conform to MutableCollection
and/or RangeReplaceableCollection
can still have mutating
methods inspired by them to alter element content and/or collection composition. Such methods for Dictionary
all have a RRC tinge to them, even the ones that could be used for MC purposes. The problem is the Dictionary
is missing a primitive, Value
mutation (but not insertion/removal) via subscript
, and that's a bug I should file soon. Here's what I mean:
extension Dictionary {
/// Accesses only the value at the specified position.
///
/// This subscript takes an index into the dictionary, instead of a key, and
/// returns the only the `value` part of the corresponding key-value tuple.
/// When performing collection-based operations that return an index into a
/// dictionary, use this subscript with the resulting value to possibly
/// mutate the value without risk of inserting or removing a key-value pair.
///
/// For example, to find the key for a particular value in a dictionary, use
/// the `firstIndex(where:)` method.
///
/// let countryCodes = ["BR": "Brazil", "GH": "Ghana", "JP": "japan"]
/// if let index = countryCodes.firstIndex(where: { $0.value == "japan" }) {
/// countryCodes[valueAt: index] = "Japan"
/// print(countryCodes[index])
/// print("Japan's country code is '\(countryCodes[index].key)'.")
/// } else {
/// print("Didn't find 'Japan' as a value in the dictionary.")
/// }
/// // Prints "("JP", "Japan")"
/// // Prints "Japan's country code is 'JP'."
///
/// - Parameter position: The position of the key-value pair to partially
/// access. `position` must be a valid index of the dictionary and not
/// equal to `endIndex`.
/// - Returns: The value part of the key-value pair corresponding to
/// `position`, but in `inout` mode.
public subscript(valueAt i: Index) -> Value {
get { return self[i].value }
set { /* ??? */ }
}
/// If the given key is found in the dictionary, use the given closure to
/// possibly mutate the corresponding value.
///
/// For example, to ensure the country codes for Japan and China are in
/// uppercase, if the corresponding country is in the dictionary:
///
/// let codeCountry = ["Brazil": "BR", "Ghana": "GH", "Japan": "jp"]
/// let uppercase: (inout String) -> Void = { $0 = $0.uppercased() }
/// var hadZhKey = codeCountry.updateValue(forKey: "China", by: uppercase)
/// var hadJpKey = codeCountry.updateValue(forKey: "Japan", by: uppercase)
/// print(hadZhKey, hadJpKey, codeCountry["Japan"])
/// // Prints 'false true "JP"'
///
/// - Parameter key: The key of the desired element.
/// - Parameter body: A closure that takes the `value` member from an
/// element of the collection as a mutable parameter.
/// - Parameter value: The mutable `value` member of the targeted element.
/// - Returns: `true` if the `key` was found, `false` otherwise.
///
/// - Postcondition: If the `key` was found, `body` was called on the
/// corresponding `value` and any changes on that value are retained.
@discardableResult
@inlinable
public mutating func updateValue(forKey key: Key, by body: (_ value: inout Value) throws -> Void) rethrows -> Bool {
guard let keyIndex = index(forKey: key) else { return false }
try body(&self[valueAt: keyIndex])
return true
}
/// Calls the given closure on each key-value pair in the dictionary in the
/// same order as a `for`-`in` loop, with an opportunity to mutate the
/// value.
///
/// The two loops in the following example produce the same output:
///
/// let wordNumbers = ["one": 1, "two": 2, "three": 3]
/// let wordNumbers2 = wordNumbers.mapValues { 2 * $0 }
/// print(wordNumbers2 == ["one": 2, "two": 4, "three": 6])
/// // Prints "true"
///
/// var wordNumbersTwo = wordNumbers
/// wordNumbersTwo.updateEach { key, value in
/// value *= 2
/// }
/// print(wordNumbersTwo == ["one": 2, "two": 4, "three": 6])
/// // Same as above
///
/// Using the `updateEach` method is distinct from a `for`-`in` loop in
/// several important ways:
///
/// 1. You cannot use a `break` or `continue` statement to exit the current
/// call of the `body` closure or skip subsequent calls.
/// 2. Using the `return` statement in the `body` closure will exit only from
/// the current call to `body`, not from any outer scope, and won't skip
/// subsequent calls.
/// 3. You can change the `value` member, without risk of disrupting any
/// retention `keys` may use when looping over all key-value pairs by
/// ensuring none are added nor removed (unlike iteration with `keys` and
/// using `updateValue(_: forKey:)`).
///
/// - Parameter body: A closure that takes an element of the sequence as a
/// parameter.
/// - Parameter key: The immutable key of the currently visited key-value
/// element pair.
/// - Parameter value: The mutable value of the currently visited key-value
/// element pair.
///
/// - Postcondition: Any mutations to any element's value are retained.
public mutating func updateEach(by body: (_ key: Key, _ value: inout Value) throws -> Void) rethrows {
var index = startIndex
let end = endIndex
while index < end {
let key = self[index].key
try body(key, &self[valueAt: index])
formIndex(after: &index)
}
}
}
Dictionary.updateEach(by:)
is what I first came up with, because that's what I needed for my work. I couldn't figure out how to implement it; that's when I realized we're missing a primitive. Staring at the various subscript
members and their mutability, I realized we need a set
-able subscript
for values, then the implementation of updateEach
became clear, and a need for the intermediate updateValue(forKey: by:)
.
I'm still looking through the Dictionary
implementation files to see if operations there can provide code for the new user-level primitive.
...
The updateEach
loop is the same as what's needed for a mutating
forEach
.
...
Glancing over the just-referenced thread: woah, the values
partial accessor conforms to MutableCollection
?!... Yes, it's that way in the current Git repo. I wonder if we could use that code somehow; there should still be a more direct way for a dictionary to mutate a value.