Swift 4 is packed with all kinds of great updates to the Swift Standard Library's Dictionary type thanks to the great work put in by Nate Cook to enhance the Dictionary and Set types in proposal 165 of Swift Evolution. With the beta available for Xcode 9 and the implementation of new APIs available now in the Swift 4 toolchain I thought I would cover a few new dictionary enhancements as well as a few common ones to help you ramp up quicker on what is new with Dictionaries in Swift 4. This tutorial is meant to be a brief overview of how to work with some new and existing APIs and is not meant to be an in-depth analysis on proposal 165 for Swift Evolution.
NOTE: This tutorial assumes you have at least Swift 4.0 installed in your Xcode toolchain or that you are using a version of Xcode that supports Swift 4, like Xcode 9 beta. To get started with this tutorial you can know a lot about Swift or a little about Swift, these examples are aimed at everyone.
1) Dictionary Creation
The first two examples of dictionary creation may look familiar as these are Swift 3, Xcode 8 supported methods for dictionary creation. The first creates a new mutable dictionary type with a string definition as the key and value. After the dictionary is created on line 6 the subscript syntax is used to explicitly assign a value to a key.
The second example creates a dictionary literal where the integer keys and values are explicitly assigned during the dictionary creation. These examples often work well when manipulating small bits of data in your program.
The third example is new to Swift 4 and came out of proposal 165. In this example an array of tuples is created to simulate key/value pairs in a dictionary that contain conflicting keys. If you notice, the first two tuples contain the "one" key which typically is not supported when creating a dictionary. This new Dictionary initializer now enhances dictionary uniqueness by using a closure to filter the first duplicate keys in a dictionary. Notice that in the output that the "one" key contains the number one instead of two.
["three": 3, "four": 4, "five": 5, "one": 1, "two": 2]
The fourth example is new to Swift 4 and came out of proposal 165. In this example an array of key strings is created as well as an array of integers that will ultimately be used to create a new dictionary of even key / value pairs. On line 34 you will notice that the numeric sequence is filtered to extract just the even values and in line 35 a new dictionary initializer is then used to map a unique set of keys and values together to form the evenDict dictionary with an output of:
["six": 6, "four": 4, "eight": 8, "ten": 10, "two": 2]
// Create a new (key: string, value: string) dictionary and add three values using the subscript syntax var stringDictionary: Dictionary = [String: String]() stringDictionary["swift"] = "Swift is a great language" stringDictionary["python"] = "Python is a great tool" stringDictionary["cpp"] = "C++ is the best language" print(stringDictionary["python"] ?? "No subscript found: 🙁") // Python is a great tool // Create a new (key: integer, value: string) dictionary literal var integerDictionaryLiteral: Dictionary = [ 45: "Swift Article", 56: "Python Article", 71: "C++ Article" ] print(integerDictionaryLiteral[71] ?? "No subscript found: 🙁") // C++ Article // Create a new unique dictionary out of an array of tuples containing duplicate keys // https://developer.apple.com/documentation/swift/dictionary/2892961-init?changes=latest_minor // Xcode 9 / Swift 4 let dupKeys = [("one", 1), ("one", 2), ("two", 2), ("three", 3), ("four", 4), ("five", 5)] let nonDupDict = Dictionary(dupKeys, uniquingKeysWith: { (first, _) in first }) print(nonDupDict) // ["three": 3, "four": 4, "five": 5, "one": 1, "two": 2] // Create a new dictionary by mapping an array of keys and values // https://developer.apple.com/documentation/swift/dictionary/2894798-init?changes=latest_minor // Xcode 9 / Swift 4 let evenKeys = ["two", "four", "six", "eight", "ten"] var sequence = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sequence = sequence.filter { $0 % 2 == 0 } let evenDict = Dictionary(uniqueKeysWithValues: zip(evenKeys, sequence)) print(evenDict) // ["six": 6, "four": 4, "eight": 8, "ten": 10, "two": 2]
2) Dictionary Modification
The first three examples of dictionary modification may look familiar as these are Swift 3, Xcode 8 supported methods for dictionary mutation. The first is just straight forward mutation via assigning a value to a subscript.
In the second example, an update is performed by using the API updateValue(_:forKey:) to update a value at a specific key. Notice that the current value is assigned to an optional as this value could be nil if the key was not present in the dictionary.
In the third example we are removing a value in a dictionary much like we updated one, by using the API removeValue(forKey:) instead to do so. Notice that in the removal example the value that was removed is also assigned to an optional value to validate that, in fact, the value removed and nil was not returned.
The forth example is new to Swift 4 and came out of proposal 165 as a new way to allow developers to merge a sequence of name value pairs into a dictionary. You will notice that in the example I have the existing dictionary and a sequence of tuples that uses a closure to sort the current dictionary values as duplicates and merge all other new data from the sequence. The following example outputs:
["three": 6, "four": 4, "five": 5, "one": 2, "two": 2]
This is a very cool addition to the dictionary type in Swift 4, but I must warn you that the documentation is pretty confusing in some cases and to make sure you are merging a sequence and not a new dictionary.
// -------- Modifying a Dictionary -------- // Updating a value in a dictionary using the subscript integerDictionaryLiteral[56] = "Python 3.6 Article" print(integerDictionaryLiteral[56] ?? "No subscript found: 🙁") // Updating a value in a dictionary using updateValue(_:forKey:) // https://developer.apple.com/documentation/swift/dictionary/1539001-updatevalue // This method of updating a dictionary will add a value to a dictionary if one does not exist if let updatedValue = integerDictionaryLiteral.updateValue("Python 3.7 is not out yet", forKey: 56) { print(updatedValue) // Python 3.6 Article print(integerDictionaryLiteral[56] ?? "No subscript found: 🙁") // Python 3.7 is not out yet } // Removing a value in a dictionary using removeValue(forKey:) // https://developer.apple.com/documentation/swift/dictionary/1641348-removevalue // This method of updating a dictionary will remove a value in a dictionary if one exists if let removedPythonValue = stringDictionary.removeValue(forKey: "python") { print(removedPythonValue) // Python is a great tool print(stringDictionary["python"] ?? "No subscript found: 🙁") // No subscript found: 🙁 } // Merging the existing dictionary with a sequence of tuples to emulate a dictionary // Xcode 9 / Swift 4 // https://developer.apple.com/documentation/swift/dictionary/2892855-merge?changes=latest_minor var existing = ["one": 2, "two": 2, "three": 6] let newData = [("one", 3), ("two", 1), ("three", 3), ("four", 4), ("five", 5)] existing.merge(newData) { (current, _) in current } print(existing) // ["three": 6, "four": 4, "five": 5, "one": 2, "two": 2]
3) Dictionary Grouping
The two grouping examples are new to Swift 4 and came out of proposal 165 as a new way to allow developers to now group a sequence of values into a dictionary that explains their grouping. The first example take the people array and creates a dictionary out of the sequence containing the first letter of the names as the key and an array as the value. The output of the first example is:
["R": ["Rich"], "K": ["Karin", "Ken"], "M": ["Matt", "Mary", "Mike"], "P": ["Phil"], "E": ["Edward"]]
The second example works much like the first, but instead of using the letter of the name as the key, the length is instead used. This functionality provides rich word grouping functionality when a procedure like language processing is performed. The output of the second example is:
[5: ["Karin"], 6: ["Edward"], 4: ["Matt", "Rich", "Mary", "Mike", "Phil"], 3: ["Ken"]]
// -------- Dictionary Grouping -------- let people = ["Matt", "Rich", "Mary", "Mike", "Karin", "Phil", "Edward", "Ken"] // Group people into arrays with the first letter of their name as the key // https://developer.apple.com/documentation/swift/dictionary/2893436-init // Xcode 9 / Swift 4 (Proposal 165) let groupedNameDictionary = Dictionary(grouping: people, by: { $0.characters.first! }) print(groupedNameDictionary) // Group the people array into arrays with similar length and the integer length as the key for the dictionary // https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md#2-key-based-subscript-with-default-value // Xcode 9 / Swift 4 (Proposal 165) let groupedLengthDictionary = Dictionary(grouping: people) { $0.utf16.count } print(groupedLengthDictionary)
Now you know more about how to create and manipulate dictionaries in Swift 4! Please feel free to download the playground example from my Github and mess around with it. One thing to note again is that if your version of Xcode does not support Swift 4 yet, you will need to download the Swift 4 toolchain to run this playground in Xcode.
Thank you very much for reading!
Credits: Cover image designed by Freepik.
Comments
On #merge
Interesting that you mention merge(_:uniquingKeysWith:) and pay attention to the fact that this method doesn't return a new dictionary but rather mutates an existing one, but why not mention merging(_:uniquingKeysWith:) then? The latter works the same as merge(_:uniquingKeysWith:) but creates a new dictionary.