В этом уроке мы возьмем список задач, отфильтруем выполненные, посчитаем минуты, сгруппируем количество по статусам через словарь и разберем, когда писать for-in, а когда удобнее map, filter, reduce
Так коллекции Swift будут не справочником, а рабочим инструментом для моделей, экранов и будущих сетевых данных
- Что получится в конце
- Array: список одного типа
- Индексы и безопасный доступ
- for-in: простой перебор
- filter, map, reduce
- Dictionary: ключ и значение
- Домашка: отчет по задачам
- Мини-практика: поиск первой невыполненной
- Частые ошибки и порядок проверки
- Безопасный доступ к элементам
- Практика: отчет по задачам
- Dictionary для группировки
- Когда for-in лучше map
- Сортировка и вывод top-листа
- Домашка: мини-аналитика по урокам
- Что может быть еще интересно по этой теме
- Что почитать дальше по Swift
Что получится в конце
Код:
struct Task {
let title: String
let minutes: Int
let isDone: Bool
}
let tasks = [
Task(title: "Learn optionals", minutes: 30, isDone: true),
Task(title: "Practice structs", minutes: 40, isDone: true),
Task(title: "Build SwiftUI screen", minutes: 50, isDone: false)
]
let doneTasks = tasks.filter { $0.isDone }
let totalMinutes = doneTasks.reduce(0) { result, task in
result + task.minutes
}
print("Done tasks: \(doneTasks.count)")
print("Minutes: \(totalMinutes)")
Ожидаемый смысл:
Done tasks: 2
Minutes: 70
Array: список одного типа
Массив Swift хранит значения одного типа:
let numbers = [1, 2, 3]
let names = ["Swift", "Xcode", "SwiftUI"]
Для своих моделей:
let tasks: [Task] = [...]
Swift часто выводит тип сам, но явная запись [Task] полезна в учебных примерах
Если массив должен изменяться:
var names = ["Swift"]
names.append("SwiftUI")
Если список не меняется, используйте let. Это та же привычка, что с обычными переменными
Индексы и безопасный доступ
К элементу можно обратиться по индексу:
let first = tasks[0]
Но если индекс за границами массива, программа упадет. Поэтому для пользовательского индекса проверяйте границы:
let index = 10
if tasks.indices.contains(index) {
print(tasks[index].title)
} else {
print("No task with this index")
}
Не полагайтесь на то, что индекс «обычно правильный». Данные от пользователя, сети или UI могут быть неожиданными
for-in: простой перебор
Цикл:
for task in tasks {
print(task.title)
}
хорош для понятного действия с каждым элементом. Если нужно вместе с индексом:
for (index, task) in tasks.enumerated() {
print("\(index + 1). \(task.title)")
}
enumerated() возвращает пары индекс-значение. Это удобно для нумерации списка в консоли или SwiftUI
filter, map, reduce
filter оставляет элементы:
let doneTasks = tasks.filter { $0.isDone }
map превращает элементы:
let titles = tasks.map { $0.title }
reduce сворачивает список в одно значение:
let minutes = tasks.reduce(0) { result, task in
result + task.minutes
}
Не нужно насильно заменять каждый цикл на map или reduce. Если цикл читается яснее, оставьте цикл. Но стандартные операции помогают писать код ближе к смыслу задачи
Dictionary: ключ и значение
Словарь хранит значения по ключам:
var counters: [String: Int] = [:]
for task in tasks {
let key = task.isDone ? "done" : "todo"
counters[key, default: 0] += 1
}
print(counters)
default: 0 означает: если ключа еще нет, взять ноль и увеличить его. Это удобный способ считать категории
Если обратиться к словарю:
let doneCount = counters["done"]
результат будет optional, потому что ключа может не быть. Вот где коллекции снова встречаются с optionals
Домашка: отчет по задачам
Сделайте функцию:
func report(tasks: [Task]) -> String
Она должна вернуть строку:
Done: 2, todo: 1, minutes: 70
Используйте filter, reduce или обычный for-in. Дополнительная задача: отсортируйте задачи по minutes через sorted
Мини-практика: поиск первой невыполненной
Swift дает метод first(where:):
let nextTask = tasks.first { !$0.isDone }
if let nextTask {
print("Next: \(nextTask.title)")
} else {
print("All tasks are done")
}
Результат optional, потому что подходящей задачи может не быть. Это хороший пример связки коллекций и безопасного извлечения
Частые ошибки и порядок проверки
Индекс вышел за границы Проверяйте tasks.indices.contains(index), если индекс приходит извне
Думаете, что filter меняет массив filter возвращает новый массив. Исходный массив остается прежним
Путаете map и compactMap map сохраняет optional-результаты, compactMap убирает nil. Не используйте compactMap, если nil несет важный смысл
Словарь возвращает optional Ключа может не быть, поэтому dictionary[key] возвращает optional-значение
Безопасный доступ к элементам
В Swift обращение по индексу строгое:
let names = ["Swift", "SwiftUI"]
print(names[2])
Такой код упадет, потому что элемента с индексом 2 нет. Это не optional-операция. Поэтому перед доступом по индексу проверяйте границы:
let index = 1
if names.indices.contains(index) {
print(names[index])
} else {
print("Index is out of range")
}
В реальном UI это важно: список может быть пустым, фильтр может вернуть меньше элементов, а пользователь может быстро менять состояние экрана
Практика: отчет по задачам
Создайте массив задач:
struct TaskItem {
let title: String
let minutes: Int
let isDone: Bool
}
let tasks = [
TaskItem(title: "Read optionals", minutes: 30, isDone: true),
TaskItem(title: "Build SwiftUI screen", minutes: 50, isDone: false),
TaskItem(title: "Make network request", minutes: 45, isDone: false)
]
Посчитайте готовые задачи:
let doneCount = tasks.filter { $0.isDone }.count
print("Done: \(doneCount) / \(tasks.count)")
Посчитайте общее время:
let totalMinutes = tasks.reduce(0) { result, task in
result + task.minutes
}
print("Total minutes: \(totalMinutes)")
Так коллекции перестают быть абстрактными. Массив хранит модели, filter выбирает нужные, reduce собирает итог
Dictionary для группировки
Словарь удобен, когда нужно быстро получить значение по ключу:
var minutesByTopic: [String: Int] = [:]
for task in tasks {
minutesByTopic[task.title] = task.minutes
}
print(minutesByTopic["Read optionals"] ?? 0)
Доступ к словарю возвращает optional, потому что ключа может не быть. Именно здесь прошлый урок про optionals начинает работать в связке с коллекциями
Если нужно увеличить счетчик, используйте default:
var countByStatus: [String: Int] = [:]
for task in tasks {
let key = task.isDone ? "done" : "open"
countByStatus[key, default: 0] += 1
}
print(countByStatus)
default: 0 говорит словарю: если ключа еще нет, начни с нуля
Когда for-in лучше map
map, filter и reduce удобны, но обычный for-in не хуже. Если внутри много шагов, логирования или условий, цикл часто читается проще:
for task in tasks {
if task.isDone {
print("Done: \(task.title)")
} else {
print("Open: \(task.title)")
}
}
Не превращайте каждую задачу в цепочку методов. Хороший Swift-код не обязан быть максимально коротким. Он должен быть понятным для следующего человека, который откроет файл
Сортировка и вывод top-листа
Еще один частый сценарий — отсортировать массив и показать несколько первых элементов:
let sortedTasks = tasks.sorted { first, second in
first.minutes > second.minutes
}
for task in sortedTasks.prefix(2) {
print("\(task.title): \(task.minutes) min")
}
sorted не меняет исходный массив, а возвращает новый. Это спокойнее для учебного кода: вы можете сравнить tasks и sortedTasks и увидеть, что исходные данные остались прежними
Если нужно изменить массив на месте, есть sort, но начинающему проще сначала пользоваться sorted. Меньше побочных эффектов, легче отлаживать результат
Домашка: мини-аналитика по урокам
Добавьте в TaskItem поле topic, например Swift, SwiftUI, Networking. Затем посчитайте, сколько минут уходит на каждую тему через словарь с default: 0
Проверка результата простая: сумма значений в словаре должна совпасть с totalMinutes. Если не совпала, значит вы потеряли одну из задач в цикле или неверно выбрали ключ
Что может быть еще интересно по этой теме
Что лучше: цикл или map/filter? Для обучения полезны оба. Цикл проще для пошаговой логики, map/filter/reduce лучше выражают типичные операции над коллекциями
Почему Swift не дает безопасный tasks[10]? Индексный доступ быстрый и строгий. Для потенциально неверного индекса проверяйте границы вручную
Когда использовать Set? Когда важна уникальность элементов, а порядок не является главным смыслом
Что дальше после коллекций? SwiftUI. Экран почти всегда показывает массивы моделей: список задач, товаров, пользователей или результатов запроса
Что почитать дальше по Swift
- Функции, struct и class в Swift
- SwiftUI: первый экран с Text, Button и @State
- @Binding и передача данных между SwiftUI views
- async await в Swift: первый сетевой запрос



