Функциональные методы: map, filter, reduce
map, filter и reduce превращают многословные циклы в одну выразительную строку. Это сердце декларативного мышления, на котором стоит SwiftUI.
Суть урока: вместо того чтобы описывать КАК перебирать коллекцию, вы описываете ЧТО хотите получить. map преобразует каждый элемент, filter отбирает нужные, reduce сворачивает всё в одно значение.
Эти методы принимают замыкание — небольшую функцию без имени, описывающую правило преобразования. Начнём с map: он берёт каждый элемент и возвращает новый, формируя новый массив той же длины.
let prices = [100, 250, 80]
let withTax = prices.map { price in price * 2 }
// [200, 500, 160]
let labels = prices.map { "\($0) руб." } // $0 — первый аргумент
// ["100 руб.", "250 руб.", "80 руб."]Запись $0 — сокращение для первого аргумента замыкания. filter отбирает элементы, для которых замыкание вернуло true:
let numbers = [3, 8, 1, 12, 5]
let big = numbers.filter { $0 > 4 }
// [8, 12, 5]reduce сворачивает коллекцию в одно значение, начиная с заданного: первый аргумент — стартовое значение, второй — правило объединения накопителя и текущего элемента:
let total = numbers.reduce(0) { acc, item in acc + item }
// 29
let sum = numbers.reduce(0, +) // ещё короче: 29Методы можно соединять в цепочку, читая её сверху вниз как конвейер данных:
let result = prices
.filter { $0 >= 100 } // оставить дорогие
.map { $0 * 2 } // удвоить
.reduce(0, +) // сложить
// (100*2) + (250*2) = 700[100, 250, 80]
| filter { $0 >= 100 }
v
[100, 250]
| map { $0 * 2 }
v
[200, 500]
| reduce(0, +)
v
700Попробуй сам ▶ — запусти код прямо в браузере (Pyodide). Здесь нет Swift, но логика та же, что под капотом мобильного кода:
# map / filter / reduce есть и в Python — мышление абсолютно то же.
from functools import reduce
prices = [100, 250, 80]
with_tax = list(map(lambda p: p * 2, prices))
print('map:', with_tax)
big = list(filter(lambda p: p >= 100, prices))
print('filter:', big)
total = reduce(lambda acc, x: acc + x, big, 0)
print('reduce:', total)
# Конвейер целиком
pipeline = reduce(lambda a, x: a + x,
map(lambda p: p * 2,
filter(lambda p: p >= 100, prices)), 0)
print('pipeline:', pipeline)Как работает под капотом
Замыкание — это самостоятельный блок кода, который можно передавать как значение. map, filter и reduce — обычные методы протокола Sequence, принимающие такое замыкание. Компилятор часто встраивает (inline) короткие замыкания, поэтому функциональный стиль обычно не медленнее ручного цикла. Эта же идея «передать поведение как данные» лежит в основе SwiftUI, где вы описываете интерфейс как функцию от состояния.
Частые ошибки
- Путать map и forEach. map возвращает новый массив; forEach просто выполняет действие и ничего не возвращает.
- Слишком длинные цепочки. Пять-шесть звеньев тяжело читать — иногда понятнее обычный цикл или промежуточная переменная.
- Забыть стартовое значение reduce. Первый аргумент обязателен и задаёт тип результата.
Best practices
- Используйте сокращения $0, $1 для коротких замыканий, но именуйте аргументы в сложных.
- Стройте читаемые конвейеры: filter перед map, чтобы преобразовывать меньше элементов.
- compactMap пригодится, чтобы преобразовать и сразу отбросить nil-результаты.
Итоги. map, filter и reduce — декларативный способ обработки данных. Они короче и выразительнее циклов, а главное — приучают мыслить в стиле «что я хочу получить», который вы будете применять во всём SwiftUI.
Шире контекста
Декларативное мышление, которое вы тренируете на map, filter и reduce, — это та же ментальная модель, что лежит в основе всего SwiftUI. Там вы описываете интерфейс как функцию от данных, здесь — преобразование данных как цепочку чистых операций. В обоих случаях вы говорите «что я хочу получить», а не «какими шагами это собрать». Кроме трёх базовых методов, в арсенале Swift есть compactMap (преобразовать и отбросить nil), flatMap (развернуть вложенные коллекции), sorted, prefix, contains и десятки других — все они складываются в выразительные конвейеры. Важная деталь производительности: ставьте filter раньше map, чтобы преобразовывать только нужные элементы. Овладев этим стилем, вы будете писать обработку данных, которая читается почти как описание задачи на естественном языке, и при этом остаётся быстрой благодаря встраиванию замыканий компилятором.