Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Let’s Functional Programing in Your Swift Code

Avatar for iamhands0me iamhands0me
August 30, 2025
260

Let’s Functional Programing in Your Swift Code

Let’s Functional Programing in Your Swift Code by UJ Cheng @ iPlayground 2025

Avatar for iamhands0me

iamhands0me

August 30, 2025
Tweet

Transcript

  1. Scenario Given an array of scores, apply square root scaling

    (√𝒔×𝟏𝟎) and sum the results that are ≥ 60 func sum(scores: [Int]) -> Double { var total: Double = 0 for score in scores { let scaled = sqrt(Double(score)) * 10 if scaled >= 60 { total += scaled } } return total } func sum(scores: [Int]) -> Double { scores .map { sqrt(Double($0)) * 10 } .filter { $0 >= 60 } .reduce(0, +) }
  2. Functional Programing Higher Order Functions Closure Expression Function Chaining Single-Expression

    (with return omitted) 1-liner 一句話 func sum(scores: [Int]) -> Double { scores .map { sqrt(Double($0)) * 10 } .filter { $0 >= 60 } .reduce(0, +) }
  3. flatMap(_:) let numbers = [1, 2, 3, 4] let mapped:

    [[Int]] = numbers.map { Array(repeating: $0, count: $0) } // [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]] let flatMapped: [Int] = numbers.flatMap { Array(repeating: $0, count: $0) } // [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
  4. let possibleNumbers = ["1", "2", "three", "///4///", "5"] let mapped:

    [Int?] = possibleNumbers.map { Int($0) } // [1, 2, nil, nil, 5] let compactMapped: [Int] = possibleNumbers.compactMap { Int($0) } // [1, 2, 5] compactMap(_:)
  5. sorted(by:) let students: Set = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]

    let descendingStudents = students.sorted(by: >) print(descendingStudents) // Prints ["Peter", "Kweku", "Kofi", "Akosua", "Abena"]
  6. reversed() let word = "Backwards" for char in word.reversed() {

    print(char, terminator: "") } // Prints "sdrawkcaB" let reversedWord = word.reversed() print(reversedWord) // Prints ReversedCollection<String>(_base: "Backwards")
  7. reversed() let word = "Backwards" for char in word.reversed() {

    print(char, terminator: "") } // Prints "sdrawkcaB" let reversedWord = word.reversed() print(reversedWord) // Prints ReversedCollection<String>(_base: "Backwards") "Backwards" String ReversedCollection<String>
  8. contains(where:) let scores = [75, 51, 84, 93, 50] if

    !scores.contains(where: { $0 < 60 }) { print("All Pass!") }
  9. allSatisfy(_:) let scores = [75, 51, 84, 93, 50] if

    scores.allSatisfy({ $0 >= 60 }) { print("All Pass!") }
  10. let students: [(name: String, score: Int)] = [ ("Alice", 75),

    ("Bob", 51), ("Carol", 84), ("Dave", 93), ("Eve", 50) ] let bestStudent = students.sorted { $0.score < $1.score }.last?.name ?? "" var bestStudent = "" var maxScore = 0 for student in students { if student.score > maxScore { bestStudent = student.name maxScore = student.score } }
  11. let students: [(name: String, score: Int)] = [ ("Alice", 75),

    ("Bob", 51), ("Carol", 84), ("Dave", 93), ("Eve", 50) ] let bestStudent = students.sorted { $0.score < $1.score }.last?.name ?? "" var maxScore = 0 for student in students { if student.score > maxScore { bestStudent = student.name maxScore = student.score } }
  12. let students: [(name: String, score: Int)] = [ ("Alice", 75),

    ("Bob", 51), ("Carol", 84), ("Dave", 93), ("Eve", 50) ] let bestStudent = students.max { $0.score < $1.score }?.name ?? "" var maxScore = 0 for student in students { if student.score > maxScore { bestStudent = student.name maxScore = student.score } } max(by:) / min(by:)
  13. prefix(_:) / suffix(_:) / dropFirst(_:) / dropLast(_:) let chars: [Character]

    = ["A", "B", "C", "D", "E", "F"] print(chars.prefix(2)) // Prints ArraySlice(["A", "B"]) print(chars.suffix(2)) // Prints ArraySlice(["E", "F"]) print(chars.dropFirst(2)) // Prints ArraySlice(["C", "D", "E", "F"]) print(chars.dropLast(2)) // Prints ArraySlice(["A", "B", "C", "D"])
  14. extension Array { subscript(safe index: Int) -> Element? { self.dropFirst(index).first

    guard index >= 0, index < count else { return nil } return self[index] } } print(chars[safe: 2]) // Prints Optional("C") print(chars[safe: 6]) // Prints nil
  15. extension Array { subscript(safe index: Int) -> Element? { self.dropFirst(index).first

    guard index >= 0, index < count else { return nil } return self[index] } } print(chars[safe: 2]) // Prints Optional("C") print(chars[safe: 6]) // Prints nil
  16. 0 1 2 3 4 5 A B C D

    E F dropFirst(2) extension Array { subscript(safe index: Int) -> Element? { self.dropFirst(index).first guard index >= 0, index < count else { return nil } return self[index] } } print(chars[safe: 2]) // Prints Optional("C") print(chars[safe: 6]) // Prints nil
  17. 0 1 2 3 4 5 A B C D

    E F dropFirst(6) extension Array { subscript(safe index: Int) -> Element? { self.dropFirst(index).first guard index >= 0, index < count else { return nil } return self[index] } } print(chars[safe: 2]) // Prints Optional("C") print(chars[safe: 6]) // Prints nil F
  18. Swift Array / filter(_:) / map(_:) / flatMap(_:) compactMap(_:) /

    reduce(_:_:) / sorted(by:) / reversed() contains(where:) / allSatisfy(_:) / min(by:) / max(by:) prefix(_:) / prefix(through:) / prefix(upTo:) prefix(while:) / suffix(_:) / suffix(from:) / dropFirst(_:) dropLast(_:) / drop(while:) / removeAll(where:) first(where:) / firstIndex(where:) / last(where:) lastIndex(where:) / forEach(_:) / enumerated() partition(by:)
  19. import Algorithms let x = [0, 1, 2, 3, 4,

    5, 6, 7, 8, 9] for i in 1 ..< x.count { print(x[i - 1], x[i]) } // Prints (0, 1) // Prints (1, 2) // Prints (2, 3) // Prints (3, 4) // Prints (4, 5) // Prints (5, 6) 2 3 4 5 6 7 8 9 0 1 1 ..< x.count
  20. import Algorithms let x = [0, 1, 2, 3, 4,

    5, 6, 7, 8, 9] for (prev, next) in x.adjacentPairs() { print(prev, next) } // Prints (0, 1) // Prints (1, 2) // Prints (2, 3) // Prints (3, 4) // Prints (4, 5) // Prints (5, 6) 8 9 x.adjacentPairs()
  21. import Algorithms let x = [0, 1, 2, 3, 4,

    5, 6, 7, 8, 9] for window in x.windows(ofCount: 3) { print(window) } // Prints ArraySlice([0, 1, 2]) // Prints ArraySlice([1, 2, 3]) // Prints ArraySlice([2, 3, 4]) // Prints ArraySlice([3, 4, 5]) // Prints ArraySlice([4, 5, 6]) // Prints ArraySlice([5, 6, 7]) x.windows(ofCount: 3) 2 3 4 5 6 7 8 9 0 1
  22. import Algorithms let x = [0, 1, 2, 3, 4,

    5, 6, 7, 8, 9] for chunk in x.chunks(ofCount: 3) { print(chunk) } // Prints ArraySlice([0, 1, 2]) // Prints ArraySlice([3, 4, 5]) // Prints ArraySlice([6, 7, 8]) // Prints ArraySlice([9]) x.chunks(ofCount: 3) 2 3 4 5 6 0 1 7 8 9
  23. import Algorithms let x = [0, 1, 2, 3, 4,

    5, 6, 7, 8, 9] for chunk in x.evenlyChunked(in: 4) { print(chunk) } // Prints ArraySlice([0, 1, 2]) // Prints ArraySlice([3, 4, 5]) // Prints ArraySlice([6, 7]) // Prints ArraySlice([8, 9]) x.evenlyChunked(in: 4) 2 3 4 5 6 0 1 7 8 9
  24. 7/5 4:54 👧 7/5 4:55 👧 7/5 5:03 👧 7/5

    7:06 👦 7/5 7:38 👦 7/5 7:38 👦 7/7 9:41 👦 7/7 9:41 👧 7/7 9:41 👧 7/7 9:42 👧 import Algorithms struct Message { let date: Date let sender: String let text: String } ForEach(messages.chunked(by: { $0.sender == $1.sender })) { chunk in ForEach(messages) { message in MessageView(message) }
  25. 👧 👧 👦 import Algorithms struct Message { let date:

    Date let sender: String let text: String } ForEach ForEach(messages.chunked(by: { $0.sender == $1.sender })) { chunk in ForEach(messages) { message in Section { ForEach(chunk) { message in MessageView(message) } } } 7/5 4:54 👧 7/5 4:55 👧 7/5 5:03 👧 7/5 7:06 👦 7/5 7:38 👦 7/5 7:38 👦 7/7 9:41 👦 7/7 9:41 👧 7/7 9:41 👧 7/7 9:42 👧 ForEach(messages.chunked(by: { $0.sender == $1.sender })) { chunk in
  26. import Algorithms struct Message { let date: Date let sender:

    String let text: String } ForEach(messages.chunked(on: \.sender), id: \.1) { sender, chunk in ForEach(messages.chunked(by: { $0.sender == $1.sender })) { chunk in Section { ForEach(chunk) { message in MessageView(message) } } header: { Text(sender) } } 👧 👧 👦 7/5 4:54 … 7/5 4:55 … 7/5 5:03 … 7/5 7:06 … 7/5 7:38 … 7/5 7:38 … 7/7 9:41 … 7/7 9:41 … 7/7 9:41 … 7/7 9:42 … ForEach(messages.chunked(on: \.sender), id: \.1) { sender, chunk in } header: { Text(sender) } sender,
  27. let senders = messages.map(\.sender).uniqued() // [ , ] let senderMessages

    = messages.uniqued(on: \.sender) // [ , ] 👧 👦 7/5 4:54 👧 7/5 7:06 👦 uniqued() / uniqued(on:)
  28. let messagesBySender: [String: [Message]] = messages.grouped(by: \.sender) // [ :

    ]. , : ] grouped(by:) 7/5 4:54 👧 7/5 4:55 👧 7/5 5:03 👧 7/5 7:06 👦 7/5 7:38 👦 7/5 7:38 👦 7/7 9:41 👦 7/7 9:41 👧 7/7 9:41 👧 7/7 9:42 👧 👧 👦
  29. let currentUser = "👦" let selfMessages = messages.filter { $0.sender

    == currentUser } let otherMessages = messages.filter { $0.sender != currentUser }
  30. let date = DateComponents(calendar: .current, month: 7, day: 7).date! let

    index = messages.partitioningIndex { $0.date > date } print(messages[index...]) // Prints ArraySlice([ , , , ]) partitioningIndex(where:) 7/7 9:41 👦 7/7 9:41 👧 7/7 9:41 👧 7/7 9:42 👧 𝑂(log 𝑛)
  31. func sqrt(x: Int) -> Int { (0 ..< x) .partitioningIndex

    { $0 * $0 >= x } } func sqrt(x: Int) -> Int { var l = 0 var r = x while l <= r { let m = (l + r) / 2 if m * m >= x { r = m - 1 } else { l = m + 1 } } return l } Scenario Compute ceiling of √𝒙 with Binary Search (Find the smallest integer ≥ √𝒙) √𝟏 = 𝟏 √𝟒 = 𝟐 √𝟏𝟎 ≈ 𝟒
  32. Swift Algorithms / adjacentPairs() / chain(_:_:) / chunked(by:) / chunked(on:)

    chunks(ofCount:) / combinations() / combinations(ofCount:) / compacted() cycled() / cycled(times:) / firstNonNil(by:) / interspersed(with:) / joined(by:) max(count:) / max(count:sortedBy:) / min(count:) / min(count:sortedBy:) minAndMax() / minAndMax(by:) / partitioningIndex(where:) / permutations() permutations(ofCount:) / product(_:_:) / randomSample(count:) randomStableSample(count:) / reductions(_:) / reductions(_:_:) reductions(into:_:) / rotate(toStartAt:) / rotate(subrange:toStartAt:) split(whereSeperator:) / split(seperator:) / stablePartition(by:) stablePartition(subrange:by:) / striding(by:) / suffix(while:) / trimming(while:) trimmingPrefix(while:) / trimmingSuffix(while:) / uniquedPermutations() uniquedPermutations(ofCount:) / uniqued() / uniqued(on:) / windows(ofCount:) https://github.com/apple/swift-algorithms
  33. Scenario Given an array of scores, count how many scores

    are above the average func count(scores: [Int]) -> Int { let average = scores.reduce(0, +) / scores.count return scores.count { $0 > average } }
  34. Scenario Given an array of scores, count how many scores

    are above the average func count(scores: [Int]) -> Int { scores.count { $0 > scores.reduce(0, +) / scores.count } }
  35. Capture List Given an array of scores, count how many

    scores are above the average func count(scores: [Int]) -> Int { scores.count { [average = scores.reduce(0, +) / scores.count] in $0 > average } }
  36. @State private var pulse: Bool = false var body: some

    View { Text(message.text) .visualEffect { [pulse] content, _ in content .blur(radius: pulse ? 2 : 0) } } Capture List Given an array of scores, count how many scores are above the average Prevent Data Races Prevent Retain Cycle [pulse] self.onEvent = { [weak self] in self?.navigationController?.popViewController(animated: true) } [weak self]
  37. @State private var pulse: Bool = false var body: some

    View { let pulse = pulse Text(message.text) .visualEffect { content, _ in content .blur(radius: pulse ? 2 : 0) } } Capture List Given an array of scores, count how many scores are above the average Prevent Data Races Prevent Retain Cycle let pulse = pulse weak var weakSelf = self self.onEvent = { weakSelf?.navigationController?.popViewController(animated: true) } weak var weakSelf = self
  38. func diff(scores: [Int]) -> Int { guard let maxPassingScore =

    scores.filter({ $0 >= 60 }).max() else { return nil } return 100 - maxPassingScore } Scenario Calculate the difference between the highest passing score (≥ 60) and 100 If no score is passing, return nil
  39. func diff(scores: [Int]) -> Int { scores .filter { $0

    >= 60 } .max() .map { (maxPassingScore: Int) -> Int in 100 - maxPassingScore } } Optional.map(_:) Calculate the difference between the highest passing score (≥ 60) and 100 If no score is passing, return nil .map { (maxPassingScore: Int) -> Int in 100 - maxPassingScore }
  40. @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var messageLabel: UILabel!

    @IBOutlet weak var dateLabel: UILabel! func setupUI(message: Message) { nameLabel.isHidden = message.sender.isEmpty nameLabel.text = message.sender messageLabel.isHidden = message.text.isEmpty messageLabel.text = message.text dateLabel.text = message.date.formatted(date: .abbreviated, time: .standard) dateLabel.accessibilityLabel = message.date.formatted(date: .long, time: .standard) } 7/5 4:54 👧 Optional.map(_:)
  41. @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var messageLabel: UILabel!

    @IBOutlet weak var dateLabel: UILabel! func setupUI(message: Message) { // Setup name label nameLabel.isHidden = message.sender.isEmpty nameLabel.text = message.sender // Setup message label messageLabel.isHidden = message.text.isEmpty messageLabel.text = message.text // Setup date label dateLabel.text = message.date.formatted(date: .abbreviated, time: .standard) dateLabel.accessibilityLabel = message.date.formatted(date: .long, time: .standard) } 7/5 4:54 👧 Optional.map(_:)
  42. @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var messageLabel: UILabel!

    @IBOutlet weak var dateLabel: UILabel! func setupUI(message: Message) { nameLabel.map { $0.isHidden = message.sender.isEmpty $0.text = message.sender } messageLabel.map { $0.isHidden = message.text.isEmpty $0.text = message.text } dateLabel.map { $0.text = message.date.formatted(date: .abbreviated, time: .standard) $0.accessibilityLabel = message.date.formatted(date: .long, time: .standard) } } Optional.map(_:) 7/5 4:54 👧
  43. Wrap up Array Higher Order Functions Swift Algorithms package Use

    Capture List or Optional.map for 1-liner Higher Order Functions Swift Algorithms Capture List Optional.map
  44. Next steps Introduce Swift Algorithms to improve your code Write

    your own 1-liner Enjoy the Follow「iamhands0me」on
  45. Two Sum Given an array of integers nums and an

    integer target Return indices of the two numbers such that they add up to target func twoSum(_ nums: [Int], _ target: Int) -> [Int] { nums .indices .combinations(ofCount: 2) .first { nums[$0[0]] + nums[$0[1]] == target } ?? [] } nums target target
  46. Two Sum Given an array of integers nums and an

    integer target Return indices of the two numbers such that they add up to target func twoSum(_ nums: [Int], _ target: Int) -> [Int] { nums .indices .firstNonNil { [group = nums.indices.grouped { nums[$0] }] i in group[target - nums[i], default: []] .first { $0 != i } .map { j in [i, j] } } ?? [] } nums target target