Part of Swift with Copilot

Swift: Capitalize and decapitalize a list

Sandy LaneSandy Lane

Video: Swift: Capitalize and decapitalize a list by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Swift with Copilot: Capitalize and Decapitalize a List

names.map { $0.capitalized } — title-case each. names.map { $0.lowercased() } — lowercase. String.uppercased() for SHOUT. The map + string-method pattern is the bread and butter of array transformations.

A two-line warmup. Pulls together arrays, closures, and string methods.

The Copilot prompt

// Capitalize and decapitalize a list of names
let names = ["alice", "BOB", "Charlie"]

Copilot completes:

let names = ["alice", "BOB", "Charlie"]

let capitalized = names.map { $0.capitalized }
let lowered = names.map { $0.lowercased() }

print(capitalized)   // ["Alice", "Bob", "Charlie"]
print(lowered)       // ["alice", "bob", "charlie"]

String case methods

"hello world".capitalized    // "Hello World" — first letter of each word
"hello world".uppercased()   // "HELLO WORLD"
"HELLO WORLD".lowercased()   // "hello world"

Note: capitalized is a property (no parens). uppercased() and lowercased() are methods (with parens). It's an inconsistency in the standard library — just memorize.

Why capitalized differs from "title case"

"the quick brown fox".capitalized
// "The Quick Brown Fox"

"a tale of two cities".capitalized
// "A Tale Of Two Cities"  ← "of" should be lowercase in most title-case styles

capitalized is per-word capitalization: first letter up, rest down. For real title case (which keeps articles, prepositions, conjunctions lowercase), no built-in. Use a third-party or write rules.

Locale-aware

"İstanbul".lowercased()                              // "i̇stanbul" — wrong for Turkish
"İstanbul".lowercased(with: Locale(identifier: "tr_TR"))   // "istanbul" — correct

Turkish has a dotted/dotless I rule different from English. For user-facing transforms in non-English locales, use lowercased(with:)/uppercased(with:).

For machine processing (logs, identifiers), the default (Unicode-default) is fine.

map: applying any transformation

map works for any function/closure that takes one argument and returns one value:

names.map(\.uppercased)     // ❌ uppercased is a method, not KeyPath
names.map { $0.uppercased() }    // ✓

KeyPaths work for properties:

names.map(\.count)          // [5, 3, 7] — character counts
names.map(\.capitalized)    // because `capitalized` is a property

For methods, you need the closure form { $0.method() }.

Trim whitespace

A common companion:

let raw = ["  alice ", "BOB", " Charlie\n"]
let cleaned = raw.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
// ["alice", "BOB", "Charlie"]

let normalized = cleaned.map { $0.lowercased() }
// ["alice", "bob", "charlie"]

trimmingCharacters(in:) removes the specified characters from both ends.

Multiple transforms in one pipeline

let pipeline = names
  .map { $0.trimmingCharacters(in: .whitespaces) }
  .map { $0.lowercased() }
  .filter { !$0.isEmpty }

Multiple maps are clean; Swift's compiler optimizes them. For very large arrays, you might collapse:

let result = names.compactMap { name -> String? in
  let trimmed = name.trimmingCharacters(in: .whitespaces).lowercased()
  return trimmed.isEmpty ? nil : trimmed
}

compactMap lets you both transform and filter (drop nils).

Mutate in place: var + index loop

var names = ["alice", "BOB"]
for i in names.indices {
  names[i] = names[i].capitalized
}

In-place mutation. Rarely needed — map is usually preferred (immutable output).

Capitalize first letter only

capitalized does each word. For only the first letter:

extension String {
  var firstCapitalized: String {
    guard let first = self.first else { return self }
    return first.uppercased() + self.dropFirst()
  }
}

"hello world".firstCapitalized
// "Hello world"

first returns Character?, dropFirst() returns Substring. The + between String and Substring works because of StringProtocol.

Filtering by case

let onlyShouts = names.filter { $0 == $0.uppercased() && $0 != $0.lowercased() }
// strings that are entirely uppercase letters

For "starts with uppercase":

let titled = names.filter { $0.first?.isUppercase == true }

first returns optional Character; isUppercase checks if it's an uppercase letter.

A cleanup utility

extension String {
  func normalizedName() -> String {
    self.trimmingCharacters(in: .whitespacesAndNewlines)
        .capitalized
  }
}

let raw = ["  alice ", "BOB", "charlie  brown"]
let clean = raw.map(\.normalizedName)
// Wait — `\.normalizedName` doesn't work because it's a method.

let clean2 = raw.map { $0.normalizedName() }
// ["Alice", "Bob", "Charlie  Brown"] — note multi-space preserved

For stricter cleaning (collapse whitespace too):

extension String {
  func normalizedName() -> String {
    let trimmed = self.trimmingCharacters(in: .whitespacesAndNewlines)
    let collapsed = trimmed.split(whereSeparator: \.isWhitespace).joined(separator: " ")
    return collapsed.capitalized
  }
}

Common stumbles

capitalized is a property; uppercased() is a method. Different syntax. Memorize.

capitalized does every word. "iOS" → "Ios". For name preservation, write your own.

Locale issues. Turkish/Azeri have special I rules. Use lowercased(with:) for non-English.

Mutating let. let arr = [...]; arr.uppercased() — the array doesn't have a method like that, only Strings inside. Use arr.map { $0.uppercased() }.

KeyPath for methods. \.uppercased doesn't work — it's a method, not a property. Use { $0.uppercased() }.

Empty string. "".capitalized == "", no crash. Same for the others.

What's next

Episode 10: Random list of 5 numbers 1–100. Covered ground for episode 2; this episode is the more polished version.

Recap

names.map { $0.capitalized } and names.map { $0.lowercased() }. capitalized is a property (no parens); uppercased()/lowercased() are methods (with parens). For Turkish/Azeri, use lowercased(with: Locale(identifier: "tr_TR")). KeyPaths only work for properties; use closures for methods. compactMap for transform + filter.

Next episode: random list 1-100.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.