Intro to iOS Development
  • Welcome
    • ☀️Introduction
    • 🐣Getting Started
    • 🐦Course Staff
  • Logistics
    • 📜Syllabus
    • 🗓️Schedule
    • 🖊️Grading
    • 🙋‍♂️Office Hours
    • Ed Discussion
  • Assignments
    • 🍼A1: Swift Basics
    • 🧑A2: Profile
    • 💬A3: ChatDev
    • 👨‍🍳A4: ChefOS
    • 👨‍🍳A4: ChefOS - SwiftUI
    • 📱Hack Challenge
      • 🏆FA23 Winners
  • Lectures
    • 1️⃣Logistics + Swift Basics
    • 2️⃣UIKit + AutoLayout
  • 3️⃣MVC + Navigation + Delegation
  • 4️⃣UITableView
  • 5️⃣UICollectionView
  • 6️⃣Networking I
  • 7️⃣Networking II
  • 8️⃣SwiftUI I
  • 9️⃣SwiftUI II
  • 🔟Persistence + SnapKit
  • 🔔(11) Notifications
  • 🌎(12) Deployment and MapKit
  • Chapters
    • 🖐️Introduction
    • ☁️Git + GitHub
      • 1️⃣Git Installation
      • 2️⃣Git Basics
      • ➕Git+
    • 🐣Swift Basics
      • 1️⃣Variables and Constants
      • 2️⃣Data Types
      • 3️⃣Operators
      • 4️⃣Data Structures
      • 5️⃣Conditionals
      • 6️⃣Loops
      • 7️⃣Functions
      • 8️⃣Closures
      • 9️⃣Optionals
    • 🧰UIKit + AutoLayout
      • 1️⃣Classes
      • 2️⃣UIKit
      • 3️⃣AutoLayout
    • 📺MVC + Navigation + Delegation
      • 1️⃣MVC
      • 2️⃣Navigation
      • 3️⃣Delegation
    • 🏓UITableView
      • 1️⃣What is a UITableView?
      • 2️⃣UITableView Setup
    • 📚UICollectionView
      • 1️⃣What is a UICollectionView?
      • 2️⃣UICollectionView Setup
    • 🌐Networking I
      • 1️⃣HTTP Requests
      • 2️⃣Callbacks
      • 3️⃣Codable
    • 🌍Networking II
      • 1️⃣Alamofire
      • 2️⃣GET Requests
      • 3️⃣POST Requests
      • 4️⃣URLSession
    • 💾Persistence + SnapKit
      • 1️⃣Persistence
      • 2️⃣SnapKit
    • 🕊️SwiftUI
      • 1️⃣Introduction to SwiftUI
      • 2️⃣Getting Started with SwiftUI
      • 3️⃣Views + Modifiers
      • 4️⃣Layouts
      • 5️⃣Navigation
      • 6️⃣Property Wrappers
    • 🎛️Widgets
      • 1️⃣Introduction to Widgets
      • 2️⃣Setting Up Widgets
      • 3️⃣Building Widgets
      • 4️⃣Configuring Widgets
    • 🧱Project Foundation
    • ✅Testing
      • 1️⃣Unit Testing
    • 👣Debugging
      • 1️⃣OSLog
      • 2️⃣Crashlytics
      • 3️⃣Analytics
    • ☁️CI/CD
      • 1️⃣Xcode Cloud
      • 2️⃣AppStore Shipping
  • Guides
    • 🔨Xcode Project Setup
    • 🎨Figma
    • 📬Postman
    • 🥥CocoaPods
    • 🧰UIKit Handbook
    • 📑Tab Views
      • UITabBarController
      • TabView
  • Work in progress
    • 🧵Concurrency
    • 2️⃣UI Testing
    • 🕐Reactive Programming
    • 🧠Memory Management
      • 🔁ARC
    • 📦Storage
    • 📣Notifications
  • Archived
    • SP24
      • Logistics
        • 📜Syllabus
        • 🗓️Schedule
        • 🖊️Grading
        • 🙋‍♂️Office Hours
      • Assignments
        • 🍼A1: Swift Basics
        • 🧑A2: Profile
        • 💬A3: ChatDev
        • 👨‍🍳A4: ChefOS
        • 📱Hack Challenge
          • 🏆FA23 Winners
      • Lecture
        • 0️⃣Course Intro + Logistics + Git Setup
        • 1️⃣Swift Basics
        • 2️⃣UIKit + AutoLayout
        • 3️⃣MVC + Navigation + Delegation
        • 4️⃣UITableView
        • 5️⃣UICollectionView
        • 6️⃣Networking I
        • 7️⃣Networking II
        • 8️⃣Persistence + SnapKit
        • 🔟SwiftUI
        • 🔢TabViews
    • FA23
      • Logistics
        • 🐣Getting Started
        • 🐦Course Staff
        • 📜Syllabus
        • 🗓️Schedule
        • 🖊️Grading
        • 🙋‍♂️Office Hours
      • Assignments
        • 🍼A1: Swift Basics
        • 🧑A2: Profile
        • 💬A3: ChatDev
        • 👨‍🍳A4: ChefOS
        • 📱Hack Challenge
          • 🏆FA23 Winners
      • Lectures
        • 0️⃣Course Intro + Logistics
        • 1️⃣Swift Basics
        • 2️⃣UIKit + AutoLayout
        • 3️⃣MVC + Navigation + Delegation
        • 4️⃣UITableView
        • 5️⃣UICollectionView
        • 6️⃣Networking I
        • 7️⃣Networking II
        • 8️⃣Persistence + SnapKit
        • 🎛️Widgets
          • 👼Introduction to Widgets
          • ⚒️Setting Up Widgets
          • 🧱Building Widgets
          • 👨‍💻Configuring Widgets
        • 🔟SwiftUI
    • SP23
      • Logistics
        • Lecture Schedule
        • Syllabus
        • Grading
        • SP23 Office Hours
      • Chapters
        • 1. Intro to Swift & Xcode
          • Lecture Handout
          • Lecture Demo
          • 🍼Project: Swift Basics
        • 2. UIKit and AutoLayout
          • Lecture Handout
          • Lecture Demo
          • 🛒Project: UIKit + AutoLayout
        • 3. Navigation, MVC, and Delegation
          • Lecture Handout
          • Lecture Demo
          • Project: Navigation + Delegation
        • 4. UITableView
          • Lecture Handout
          • Lecture Demo
          • Project: UITableView
        • 5. UICollectionView
          • Lecture Handout
          • Lecture Demo
          • Project: UICollectionView
        • 6. Networking I
          • Lecture Handout
          • Lecture Demo
          • Project: Persistence
        • 7. Networking II
          • Lecture Handout
          • Lecture Demo
            • Message Board
          • Project: Networking II
        • 8. Swift UI
        • 9. CocoaPods
          • 🍫Lecture Handout
          • 🧑‍🍳Lecture Demo
      • Cheat Sheets
        • Setting Up a New Xcode Project
        • Submitting Your Projects
        • Setting Up CocoaPods
    • 2022
      • SwiftUI
    • 2021
      • Adding Flare
      • Project: UIView Animations (Optional - Extra Credit)
      • UIView Animations
      • Xcode Tips and Tricks
    • 2019
      • Firebase
      • Persistence: UserDefaults
  • Swift Guide (ARCHIVED)
    • About this Textbook
    • Documentation
    • Constants and Variables
      • Variable Properties
      • Lazy and Static Variables
    • Functions
    • Ranges
    • Arrays
      • Basic Array Operations
      • Iteration and Enumeration
      • Advanced Array Operations
    • Tuples
    • Conditions and While Loops
    • For Loops
    • Enums and Switches
      • Enums with Associated Values
      • Indirect Enums
    • Classes and Structs
    • Optionals
    • Dictionaries
      • Dictionary Implementation
    • Closures
    • Constraints
    • Generics
    • Protocols
      • Protocols With Associated Types
    • Casting
    • Errors
    • Networking
      • Result
    • Inout
Powered by GitBook
On this page

Was this helpful?

Export as PDF
  1. Swift Guide (ARCHIVED)
  2. Arrays

Advanced Array Operations

Advanced Array Functions

So far we have been doing all of our work on arrays based on elements and their location in an array. But, what if we don't know where an element is?

First

There will be times when you want to get an element from an array, but you don't where it is. Instead, you may know what particular field of that element is, or a property of that element. In this case, we create an anonymous function that will return true if the given element satisfies some criteria and false otherwise. This function will be applied to every element in the list, in order, until an element meets the requirement. Execution then stops and the element is returned. If no element in the list fulfills the criteria, the result will be nil. For this, we use first(where predicate: (Element) throws -> bool) rethrows -> Element?. This is one scary function declaration–let's pick it apart. first takes one argument of type (Element) throws -> bool. This argument takes in our element and has the option to throw an error, and otherwise, must return a boolean. We can ignore the error because this is a pretty niche case. Rethrows just means, that the function takes in a function that can throw an error, and if that is the case, it may just throw the error itself, so again, we can ignore this. first(where: (Element) -> bool) -> element this is a lot easier to tackle. Okay so let's put it to use.

let myList = ["a", "b", "c", "d", "e"]
// myList: [String] = ["a", "b", "c", "d", "e"]

let firstE: String? = myList.first { element in element == "e" }
// firstE: String? = optional("E")

When you use this, of course, you would want to just use a property instead of the entire instance because then what's the point of getting it out the list if you already have it. So maybe we have something more like:

struct Person {
    var name: String
    other properties ...
}
var people: [Person] = ...

let noah = people.first { person in person.name == "Noah" }

There are other cases where it may actually be more helpful to get the location in the list instead of the actual element. This is the exact same process, except instead of using first(_:) we use firstIndex(_:).

If a list stores an equatable type–meaning that it conforms to Equatable we can simplify this slightly–but just slightly. Instead of use firstIndex(where:), we can use firstIndex(of: Element). As the function header suggests, instead of creating a closure to determine criteria, we create an item and use equality for criteria. You can think of this as:

extension Array where Element: Equatable {
    
    func first(of element: Element) -> Element? {
        self.first(where: { $0 == element })
    }
    
}

You'll notice that weird where–this is a slight exposure of Array's implementation. Arrays conform to a protocol with an associated type called Element. This is an advanced technique that you do not need to understand–although if you're interested see Protocols with Associated Types. To understand this block of code, think of Array, again, as a generic with the parametric type named Element. When this type conforms to equatable (when we have access to the == function), we will provide another function that takes in an element and applies it, with ==, to first(where:). Hopefully, the code is a little easier to understand than my explanation.

Last

This is exactly the same as First, but it returns the last element that fulfills the critera–so execution never stops short of the length of the list (i.e. it is always exactly O(n)). For last, just change the function name from first to last. So, firstIndex becomes lastIndex and etc. I'm not going to re-explain everything here, so if you're curious about a function look at the previous section.

RemoveAll

Well there is removeAll(), which quite literally removes everything. But more helpfully, there is removeAll(where shouldBeRemoved: (Element) throws -> Bool) rethrows. Hmmm... This looks awfully familiar. Referring back to our decomposition of first, we can simplify this function by throwing out the error handling: removeAll(where: (Element) -> Bool). So this function takes in another function that takes in an argument and returns a bool. Aka we are taking in a function that will either determine if that element should be removed or not and just applies that to every element in a list.

Going back to our Person data structure:

var people: [Person] = ...
let noNoahs = people.removeAll { person in person.name == "Noah" }

Contains

If we want to see if an array contains an element that fulfills some critera, we use the contains function. This is exactly like first, last, everything we've talked about so far. Here's the header: contains(where predicate: (Element) throws -> Bool) rethrows. Additionally, if Element conforms to Equatable, we can use contains(_: Element) -> Bool. SeefirstIndex(of: Element) for an explanation.

Map

Map is a common function that maps from one list to another. The idea is that you apply a function that transforms an element into an element of another kind. This function is then applied to everything in a list, so the entire list is transformed. Maps type is map(_ transform: (Element) throws -> T) rethrows [T]. We can re-imagine map as:

extension Array {
    
    func map<T>(_ transform: (Element) throws -> T) rethrows -> [T] {
        var nextLst = [T]()
        for i in self {
            try nextLst.append(transform(i))
        }
        return nextLst
    }
    
}

Or a more simple version:

extension Array {
    
    func map<T>(_ transform: (Element) -> T) -> [T] {
        var nextLst = [T]()
        for i in self {
            nextLst.append(transform(i))
        }
        return nextLst
    }
    
}

Filter

Filter does pretty much exactly what you expect it's going to do. It goes through the array, checks criteria, and keeps elements that pass the critera.

Reduce

Reduce will iterate through the list and will return some form of accumulation. OCaml programmers think fold_left and fold_right. Reduce takes in an initial state and a function. Its accumulator starts at the initial state. Then it iterates through the list, applies the function to the accumulator and the next element in the list, and stores the result in the accumulator. It continues until the list is done. Think of reduce as:

extension Array {
    
    func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result {
        var accumulator = initialResult
        for i in self {
            accumulator = try nextPartialResult(accumulator, i)
        }
        return accumulator
    }
// or a simpler version
    func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) -> Result) -> Result {
        var accumulator = initialResult
        for i in self {
            accumulator = try nextPartialResult(accumulator, i)
        }
        return accumulator
    }
    
}

Mixing map, filter, and reduce can feel so cool–while potentially inefficient. Let's try to do it. Say I have a list of words and integers in string form. I want to see the sum of even numbers in the list. Here's one way to do it:

let myList = ["0", "my", "1", "name", "2", "is", "3", "noah", "4"]
let myFilteredList = myList.filter {
    if let result = Int($0), result % 2 == 0 {
        return true
    }
    return false
}
let myIntegerList = myFilteredList.map { Int($0)! }
let myResult = myIntegerList.reduce(0, +)
// 6: Int

A few notes:

  1. Int(_: String) is an optional initializer.

  2. I can forceunwrap here because I already checked that I could transform the elements into an integer

And here it is as a one-liner

myList.filter({ if let result = Int($0), result % 2 == 0 { return true }; return false }).map({ Int($0)! }).reduce(0, +)

That's quite a long one-liner because of the optional... Let's fix that!

Compact Map

compactMap is exactly like map, except if result is nil, it will automatically be filtered!

let myList = ["0", "my", "1", "name", "2", "is", "3", "noah", "4"]
let myIntegers = myList.compactMap({ Int($0) })
myIntegers: [Integers] = [0, 1, 2, 3, 4]

With this new tool, let's try that one-linear again!

myList.compactMap({ Int($0) }).filter({ $0 % 2 == 0 }).reduce(0, +)
// 6: Int

PreviousIteration and EnumerationNextTuples

Last updated 3 years ago

Was this helpful?