Collections and Data ProcessingIntroductionMost applications process groups of related values rather than isolated values. A weather app stores temperatures, a shopping app stores products, and a messaging app stores users or conversations. Swift provides three principal collection types—arrays, sets, and dictionaries—each designed around a different relationship between data items. Choosing an appropriate collection is a design decision. Arrays preserve order and permit duplicate values; sets represent unique, unordered membership; dictionaries associate keys with values. Swift also provides powerful operations for traversing and transforming collections, including iteration, subscripting, sorting, filtering, mapping, and reduction. This chapter develops practical foundations for using these tools safely and efficiently in iOS applications. Learning ObjectivesBy the end of this chapter, you should be able to: - create, modify, and access arrays, sets, and dictionaries;
- explain ordering, uniqueness, and key–value association;
- use iteration and subscripting appropriately;
- sort and transform collection data;
- apply
filter, map, and reduce to common programming tasks; - select a collection type based on application requirements; and
- identify common errors involving optionals, mutation, and collection performance.
Collection Types and Their Design MeaningA collection stores multiple values of the same element type, although a dictionary may have different key and value types. The choice of collection communicates the intended meaning of the data. | Type | Order | Duplicates | Access model | Typical use |
|---|
| Array | Preserved | Allowed | Integer index | Ordered lists, sequences | | Set | Not guaranteed | Not allowed | Membership | Tags, permissions, unique identifiers | | Dictionary | Key-based association | Keys unique | Key | Lookup tables, records |
Swift collections are generic. For example, Array<String> contains strings, while [Int] is shorthand for an array of integers. Strong typing helps detect incompatible values before an application runs. Swift collections are also value types. Assigning a collection to another variable creates an independent logical value. Swift uses copy-on-write optimization, so physical copying is generally delayed until one copy is modified. This provides value semantics without unnecessarily copying storage. Arrays: Ordered SequencesAn array is an ordered collection whose elements are accessed using integer indices beginning at zero. var tasks: [String] = ["Plan", "Code", "Test"]
tasks.append("Deploy")
tasks.insert("Design", at: 0)
print(tasks[1])The array now contains "Design", "Plan", "Code", "Test", and "Deploy". The expression tasks[1] returns the second element because indexing starts at zero. Arrays can be initialized in several ways: let emptyScores = [Int]()
let repeatedScores = Array(repeating: 0, count: 4)
let scores = [72, 88, 91, 65] let prevents structural mutation, while var permits operations such as append, remove, and replacement.
var scores = [72, 88, 91, 65]
scores[0] = 75
scores.remove(at: 3)
scores.removeLast() Accessing an invalid index causes a runtime error. Therefore, code should validate an index before subscripting when its safety is uncertain: let index = 2
if scores.indices.contains(index) {
print(scores[index])
}Useful array properties and methods include count, isEmpty, first, last, contains, and randomElement(). The last two return information safely; first, last, and randomElement() return optionals because the array may be empty. if let firstScore = scores.first {
print("First score: \(firstScore)")
}Array Complexity and Practical SelectionAppending to the end of an array is normally efficient because array storage is contiguous and may reserve extra capacity. Inserting or removing near the beginning can require later elements to move. Consequently, arrays are appropriate for ordered data and frequent indexed access, but not always for frequent insertion at the front. Sets: Unique MembershipA set stores distinct values without providing a meaningful positional order. Elements must conform to Hashable, allowing Swift to determine membership efficiently. var categories: Set<String> = ["Swift", "iOS", "Programming", "Swift"]
print(categories.count) // 3
categories.insert("Testing")
categories.remove("iOS")The duplicate "Swift" is retained only once. Set membership is tested with contains: if categories.contains("Swift") {
print("Swift is selected")
}A set is preferable when uniqueness and membership are more important than order. For example, a user’s selected permissions should not contain the same permission twice. Sets support mathematical operations: let iosTopics: Set = ["Swift", "UIKit", "Testing"]
let sharedTopics: Set = ["Swift", "Testing", "Git"]
let common = iosTopics.intersection(sharedTopics)
let all = iosTopics.union(sharedTopics)
let onlyIOS = iosTopics.subtracting(sharedTopics) common contains values in both sets, all contains values in either set, and onlyIOS contains values in the first set but not the second. Because sets are unordered, use sorted() when a predictable display order is required.
Dictionaries: Key–Value AssociationA dictionary stores associations between unique keys and values. It is useful when the application needs to find a value by an identifier rather than by position. var prices: [String: Double] = [
"Book": 18.50,
"Cable": 9.99
]
prices["Case"] = 24.00
prices["Cable"] = 11.49Dictionary subscripting returns an optional value because a key may not exist: if let price = prices["Book"] {
print(price)
}The nil value can remove a key–value pair: prices["Case"] = nil An alternative is updateValue(_:forKey:), which returns the previous value if one existed. This is useful when an application needs to know whether an update replaced existing data. Dictionaries can be traversed as key–value pairs: for (product, price) in prices {
print("\(product): \(price)")
}Dictionary iteration order should not be treated as a user-interface ordering rule. If sorted output is needed, explicitly sort the keys or pairs. for product in prices.keys.sorted() {
print("\(product): \(prices[product]!)")
}Force unwrapping is safe here only because product came from the same dictionary. In general, optional binding is safer. IterationThe basic for-in loop visits each element: let names = ["Ava", "Ben", "Chen"]
for name in names {
print(name)
}Use enumerated() when both the position and value are required: for (index, name) in names.enumerated() {
print("\(index): \(name)")
}Use where to restrict iteration: for score in scores where score >= 70 {
print("Passing: \(score)")
}A while loop is sometimes suitable for index-based processing, but direct collection iteration is usually clearer and less vulnerable to index errors. Subscripts and Safe AccessSubscript syntax provides concise access to collection elements. Arrays use integer indices, dictionaries use keys, and strings use specialized indices rather than ordinary integers. var profile = ["name": "Mina", "role": "Developer"]
profile["role"] = "iOS Developer" A useful safety practice is to avoid assuming that data exists. Dictionary access, first, and similar operations produce optionals precisely because absence is a valid state. Handle that state explicitly with if let, guard let, nil-coalescing (??), or optional mapping. let displayName = profile["name"] ?? "Guest" Sorting and Orderingsorted() returns a new collection and leaves the original unchanged. sort() mutates an array in place.
let values = [7, 2, 9, 4]
let ascending = values.sorted()
let descending = values.sorted(by: >) For custom records: struct AppUser {
let name: String
let age: Int
}
let users = [
AppUser(name: "Ava", age: 28),
AppUser(name: "Ben", age: 21)
]
let byAge = users.sorted { $0.age < $1.age }Sorting generally requires comparison of elements and has typical time complexity O(n log n), although exact performance depends on implementation and input. Sorting should therefore be performed deliberately, especially for large data sets or repeated user-interface updates. Filtering, Mapping, and ReductionSwift’s higher-order collection methods express common data-processing operations clearly. Filteringfilter returns elements satisfying a condition.
let scores = [55, 72, 91, 64, 88]
let passing = scores.filter { $0 >= 60 }The result is [72, 91, 64, 88]. The original array is unchanged. Mappingmap transforms every element and returns one output for each input.
let percentages = scores.map { "\($0)%" }The result is an array of strings. Mapping does not remove elements; if one input must produce zero or more outputs, flatMap may be appropriate. Reductionreduce combines all elements into one result. It requires an initial value and a combining operation.
let total = scores.reduce(0) { partialTotal, score in
partialTotal + score
}The initial value is 0, and each score is added to the running total. The average can then be calculated safely: let average = scores.isEmpty ? 0.0 : Double(total) / Double(scores.count) The conditional is important because division by zero is not valid. Here, total is an integer, so conversion to Double is necessary when a fractional average is desired. A reduction can also build a dictionary, although a loop may be clearer when the logic is complex: let words = ["Swift", "iOS", "Swift"]
let frequencies = words.reduce(into: [String: Int]()) { result, word in
result[word, default: 0] += 1
}reduce(into:) mutates the accumulating result efficiently within the operation.
Chaining OperationsOperations can be chained when each stage has a clear purpose: let highScoreLabels = scores
.filter { $0 >= 70 }
.sorted(by: >)
.map { "Score: \($0)" }The processing order matters: values are filtered, then sorted, then transformed. Chaining improves readability when names describe the result and closures remain simple. Choosing the Appropriate CollectionA practical selection process asks three questions: - Does order matter?
- Can duplicates occur?
- How will values be located?
Choose an array when order, duplicates, or positional access matters. Choose a set when values must be unique and the principal operation is membership testing or set algebra. Choose a dictionary when each value is identified by a unique key. For example, an online store might use: - an array for the ordered items in a shopping cart;
- a set for product categories selected by a user; and
- a dictionary keyed by product ID for quick product lookup.
Sometimes more than one collection is appropriate. A dictionary can provide lookup by ID, while an array maintains display order. Such duplication requires a clear synchronization strategy to avoid inconsistent data. Applied Case: Processing Assessment ResultsBackgroundConsider an illustrative iOS learning application that records quiz scores for a class. The application must identify passing learners, display results from highest to lowest, calculate the class average, and prevent duplicate course tags. ProblemThe raw data consists of learner names and scores. The app also receives course tags from several screens, some of which may be repeated. Analysis and SolutionA dictionary associates each learner with a score. A set removes duplicate tags. Collection methods process the scores: let results: [String: Int] = [
"Ava": 86,
"Ben": 59,
"Chen": 74,
"Dina": 91
]
let passingLearners = results
.filter { $0.value >= 60 }
.sorted { $0.value > $1.value }
let total = results.values.reduce(0, +)
let average = results.isEmpty ? 0.0 : Double(total) / Double(results.count)
let tags: Set<String> = ["swift", "ios", "swift", "collections"]passingLearners is a sorted array of key–value pairs. The set contains three unique tags. The average is computed using:
average = total ÷ number of learners For these results, the total is 310 and the number of learners is 4, so the average is 77.5 points. The solution separates concerns: the dictionary models association, the set models uniqueness, and array-producing methods support ordered presentation. Outcome and Learning PointsThe application can display passing learners in descending score order, report the average, and show each course tag once. The case demonstrates that collection choice should reflect domain meaning rather than personal preference. It also shows why optional and empty-collection cases must be considered before production use. Best Practices, Challenges, and LimitationsPrefer immutability when possible. Declare collections with let when they do not need mutation. This reduces accidental state changes and makes code easier to reason about. Avoid force unwrapping by default. A missing dictionary key or empty array is normal in many applications. Use safe handling unless the invariant is genuinely guaranteed. Do not rely on unordered output. Sets and dictionaries should be sorted before presentation when users require stable ordering. Choose expressive transformations. filter, map, and reduce are valuable, but a conventional loop may be more readable for multi-stage validation, error handling, or complex side effects. Consider memory and performance. Collection operations often create intermediate collections. For very large or lazily processed data, lazy sequences can defer work, but they should be introduced only when their behavior is understood and measured. Keep UI work responsive. Expensive sorting or processing should not block the main thread in an iOS application. Large data sets may require background processing and carefully coordinated UI updates. Validate external data. Collection contents received from networks, files, or users should be decoded and validated before use. A dictionary is not a substitute for a validated data model. Contemporary RelevanceModern Swift development frequently combines collections with Codable, asynchronous APIs, and Swift concurrency. Network responses are decoded into arrays or dictionaries, filtered before display, and transformed into view models. With concurrency, collection processing may occur away from the main actor, while UI updates must respect thread-safety rules. These developments do not change the fundamental principles: model data accurately, handle absence safely, and select structures according to required operations. Chapter SummaryArrays model ordered, indexable sequences and permit duplicates. Sets model unique membership without dependable positional order. Dictionaries model key–value associations and return optionals when a key may be absent. Swift supports safe and expressive processing through iteration, subscripting, sorting, filtering, mapping, and reduction. sorted() creates an ordered result, filter selects elements, map transforms every element, and reduce combines elements into one result. Collection selection should follow the data’s meaning and the operations the application performs. Robust code also considers empty collections, invalid indices, optionals, ordering requirements, mutation, performance, and UI responsiveness. Key TermsArray: An ordered collection that permits duplicate elements. Set: A collection of unique elements with no meaningful positional order. Dictionary: A collection associating unique keys with values. Element: An individual value stored in a collection. Index: A position used to access an array element. Key: An identifier used to access a dictionary value. Value: Data associated with a dictionary key. Subscript: Syntax used to access or modify collection data. Iteration: Repeatedly visiting elements in a collection. Mutation: Changing a collection after it has been created. Optional: A type representing either a value or absence of a value. filter: An operation that retains elements satisfying a condition. map: An operation that transforms every element. reduce: An operation that combines elements into one result. Sorting: Arranging elements according to a comparison rule. Hashable: A protocol enabling values to be used efficiently in hashed collections such as sets and dictionary keys. Value semantics: Behavior in which a collection variable represents an independent logical value. Copy-on-write: An optimization that delays physical copying until mutation occurs. Review QuestionsShort Answer Questions- What is the principal difference between an array and a set?
- Why does dictionary subscripting return an optional value?
- What does the
filter method return? - When should
sorted() be preferred to sort()? - Why is an initial value required by
reduce?
Long Answer Questions- Compare arrays, sets, and dictionaries in terms of order, uniqueness, access, and suitable applications.
- Explain how
filter, map, and reduce differ, using a Swift example for each. - Discuss safe subscripting and optional handling in collection-based programs.
- Explain why an application might use both an array and a dictionary for the same domain data.
- Describe performance and readability considerations when chaining collection operations.
Analytical / Application Questions- Design collection types for a shopping cart, a unique permission list, and a product lookup service. Justify each choice.
- Write Swift code that receives an array of integers, retains even values, sorts them descending, and converts them into display strings.
- A learner-result dictionary may be empty and may contain failing scores. Explain how you would calculate a safe average and produce a sorted list of passing learners.
Multiple Choice Questions with AnswersQuestion 1Which collection preserves order and allows duplicate elements? A. Array B. Set C. Dictionary D. Tuple Correct Answer: A. Array Question 2What does filter do to a collection? A. Sorts every element B. Retains elements satisfying a condition C. Combines all elements into one value D. Associates elements with keys Correct Answer: B. Retains elements satisfying a condition Question 3Why is a dictionary lookup such as prices["Book"] optional? A. Dictionary values are always integers B. The key may not exist C. Dictionaries cannot store strings D. Keys are accessed by array index Correct Answer: B. The key may not exist Question 4Which operation returns a new sorted collection without modifying the original array? A. sort() B. insert() C. sorted() D. shuffleInPlace() Correct Answer: C. sorted() Question 5What is the result of inserting a duplicate value into a Set? A. A second copy is always added B. The set becomes an array C. The value is stored only once D. The program must crash Correct Answer: C. The value is stored only once Question 6Which operation transforms every input element into an output element? A. map B. filter C. reduce D. remove Correct Answer: A. map Question 7What is the total produced by [3, 5, 7].reduce(0, +) ? A. 10 B. 12 C. 15 D. 21 Correct Answer: C. 15 Question 8Which collection is most appropriate for looking up a product by a unique product ID? A. Array B. Set C. Dictionary D. Range Correct Answer: C. Dictionary Question 9What does scores.first provide when scores is an array? A. Always the first integer B. An optional first element C. The array’s last index D. A Boolean membership result Correct Answer: B. An optional first element Question 10Which statement about sort() is correct? A. It always returns a dictionary B. It mutates the array in place C. It removes duplicate elements D. It can be used only with sets Correct Answer: B. It mutates the array in place
|