NEW! Master Swift design patterns with my latest book! >>

Five useful methods of dictionaries

Paul Hudson       @twostraws

Dictionaries are one of Swift’s most commonly used types, so it’s worth taking a few minutes to get to know them a bit better – even discovering one or two new techniques can make a big difference to your projects.

In this article I want to introduce you to five dictionary methods that deserve to be used more. None of them are complicated, so you should be able to read this whole thing in under ten minutes.

1. Transforming values

You should already be familiar with the map() method of sequences (if not, see here), but it’s less useful with dictionaries because it’s possible you might create a duplicate dictionary key as part of your transformation.

As a result, dictionaries get a bonus method called mapValues(): this transforms each value inside a dictionary, putting the transformed result into a new dictionary using the original keys. This gives us the transforming behavior of map() without the risk of duplicate keys.

As an example, here’s a dictionary of strings and integers that store results from an exam:

let results = ["Meghan": 80, "Chris": 90, "Charlotte": 95]

If we wanted to format those values into strings, we could use mapValues() like this:

let formattedResults = results.mapValues { "Score: \($0)" }

Once that runs, Meghan’s value will be “Score: 80” rather than just 80.

2. Intelligent merging

If you have two dictionaries of the same type and want to merge them into a single array, you might be tempted to try this:

let first = ["a": 1, "b": 2]
let second = ["c": 3]
let third = first + third

Sadly that doesn’t work, because Swift can’t tell whether it will result in duplicated keys. Instead, you should use the merge() and merging() methods – the former merges one dictionary with another in place, and the latter returns a new dictionary.

These methods are possible because they accept a second parameter: a closure that is able to resolve key clashes. This will be given the existing value and the value that would overwrite it, and you get to choose which one you want.

Here’s an example of creating one dictionary then merging another into it:

var hexColors1 = ["red": "#ff0000", "green": "#00ff00"]
let hexColors2 = ["blue": "#0000ff"]
hexColors1.merge(hexColors2) { (_, second) in second }

Using (_, second) in second as the merge resolution closure means “when trying to write an existing value with a new one, always use the new one.”

3. Grouping sequences

Dictionaries have a marvelous initializer called Dictionary(grouping:), and its job is to convert a sequence into a dictionary based on any grouping you want.

As an example, here’s an array of popular Swift conferences:

let conferences = ["AltConf", "App Builders", "NSSpain"]

We could group that into a dictionary, using the first letter of each conference as the key and the conference names stored as an array:

let alphabetical = Dictionary(grouping: conferences) { $0.first! }

That will make alphabetical equal to ["N": ["NSSpain"], "A": ["AltConf", "App Builders"]].

Alternatively, we could group the conferences based on the length of their names, like this:

let length = Dictionary(grouping: conferences) { $0.count }

That will give length the value [7: ["AltConf", "NSSpain"], 12: ["App Builders"]].

4. Filtering data

Earlier versions of Swift used to make filtering dictionaries return an array of tuples, but this has been cleaned up since Swift 4.0.

Nowadays, filtering dictionaries works like you’d expect: your closure is passed the key and value for each element, and any you return true for is included in a resulting dictionary.

For example, this would filter our exam results dictionary so that it includes only students who scored 85 or over:

let passes = results.filter { key, value in
    return value >= 85
}

5. Reserving capacity

Modifying dictionaries can be costly in Swift, particularly if you modify them repeatedly – you might end up reallocating RAM many times without realizing it.

If you know roughly how many items your dictionary needs – even a vague guess, really – then you should call reserveCapacity() on it before you start adding items. This ensures that your dictionary has contiguous storage allocated for at least the requested number of items so that Swift won’t need to keep reallocating memory.

As an example, if we were asking users to enter in state capitals for the US, we might write code like this:

var stateCapitals = [String: String]()
stateCapitals.reserveCapacity(50)

That reserves enough space for the dictionary to hold 50 items, which will be exactly enough.

Note: This is a minimum amount – you can of course go over, at which point Swift will just reallocate RAM as needed. However, this method is worth using if you even have a vague idea of how much you need.

 

MASTER SWIFT NOW
Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

About the author

Paul Hudson is the creator of Hacking with Swift, the most comprehensive series of Swift books in the world. He's also the editor of Swift Developer News, the maintainer of the Swift Knowledge Base, and Mario Kart world champion. OK, so that last part isn't true. If you're curious you can learn more here.

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>