← Назад к вопросам

В чем разница между Map, flatMap и filterMap?

2.0 Middle🔥 171 комментариев
#Коллекции и структуры данных#Язык Swift

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Map, flatMap и filterMap в iOS разработке

В Swift, Map, flatMap и filterMap (который в современном Swift был разделён на compactMap и filter) — это высокоуровневые функции для работы с коллекциями, основанные на концепциях функционального программирования. Они позволяют трансформировать, фильтровать и обрабатывать данные в декларативном стиле.

Основные отличия

ФункцияНазначениеВозвращаемый типОсобенности
mapТрансформация элементовМассив такого же размераПреобразует каждый элемент
flatMapТрансформация с "разворачиванием"Уплощённый массивУбирает вложенность опциональных и массивов
filterMapФильтрация + трансформацияМассив без nil-значенийУдаляет nil и преобразует оставшиеся

Подробное объяснение с примерами

1. map - Преобразование элементов

Функция map применяет заданное замыкание к каждому элементу коллекции и возвращает новый массив с результатами. Размер выходного массива всегда равен размеру входного.

// Пример 1: Базовое использование map
let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }
print(squared) // [1, 4, 9, 16, 25]

// Пример 2: Преобразование типов
let strings = numbers.map { String($0) }
print(strings) // ["1", "2", "3", "4", "5"]

// Пример 3: Преобразование с получением индекса
let indexed = numbers.enumerated().map { (index, value) in
    return "\(index): \(value)"
}
print(indexed) // ["0: 1", "1: 2", "2: 3", "3: 4", "4: 5"]

2. flatMap - Трансформация с "уплощением"

Функция flatMap выполняет две операции: сначала применяет трансформацию (как map), а затем "разворачивает" (flattens) вложенные коллекции в одноуровневый массив. В Swift 4.1+ flatMap для обработки опционалов был заменён на compactMap.

// Пример 1: Уплощение вложенных массивов
let nestedArrays = [[1, 2, 3], [4, 5], [6]]
let flattened = nestedArrays.flatMap { $0 }
print(flattened) // [1, 2, 3, 4, 5, 6]

// Пример 2: Трансформация с уплощением
let result = nestedArrays.flatMap { array in
    array.map { $0 * 2 }
}
print(result) // [2, 4, 6, 8, 10, 12]

// Пример 3: Использование с опциональными значениями (устаревший способ)
let optionalStrings = ["1", "2", "три", "4"]
let oldWay = optionalStrings.flatMap { Int($0) } // В Swift 4.1+ используйте compactMap

3. compactMap (ранее filterMap) - Фильтрация nil + трансформация

Функция compactMap (ранее называлась filterMap, а в Swift 4.0 и ранее — flatMap для опционалов) выполняет трансформацию, а затем удаляет все nil значения из результата. Это особенно полезно при работе с опциональными значениями.

// Пример 1: Базовое использование compactMap
let strings = ["1", "2", "three", "4", "five"]
let numbers = strings.compactMap { Int($0) }
print(numbers) // [1, 2, 4] - "three" и "five" игнорируются

// Пример 2: Цепочка преобразований
let data = ["10", "20", "invalid", "30"]
let processed = data
    .compactMap { Int($0) }  // Убираем невалидные числа
    .map { $0 * 3 }          // Умножаем оставшиеся
print(processed) // [30, 60, 90]

// Пример 3: Работа с опциональными свойствами
struct User {
    let name: String
    let email: String?
}

let users = [
    User(name: "Alice", email: "alice@example.com"),
    User(name: "Bob", email: nil),
    User(name: "Charlie", email: "charlie@example.com")
]

let emails = users.compactMap { $0.email }
print(emails) // ["alice@example.com", "charlie@example.com"]

Ключевые отличия и когда что использовать

  • Используйте map, когда нужно преобразовать каждый элемент коллекции в новый тип или значение, сохраняя структуру и размер коллекции.

  • Используйте flatMap, когда работаете с вложенными коллекциями (массивами массивов) и хотите получить одноуровневый массив, или когда ваша трансформация возвращает опциональные значения, которые нужно "развернуть" (в Swift 4.0 и ранее).

  • Используйте compactMap, когда ваша трансформация может вернуть nil, и вы хотите автоматически отфильтровать эти значения, получив массив только из не-nil результатов.

Практические примеры использования

// Реальный пример: обработка данных из сети
func processUserData(_ jsonData: [[String: Any]]) -> [String] {
    return jsonData
        .compactMap { $0["email"] as? String }  // Извлекаем только валидные email
        .map { $0.lowercased() }                 // Приводим к нижнему регистру
        .filter { $0.contains("@") }             // Фильтруем некорректные email
}

// Пример: цепочка преобразований
let input = [["value": "10"], ["value": "invalid"], ["value": "30"]]
let output = input
    .compactMap { $0["value"] as? String }  // compactMap: извлекаем String значения
    .compactMap { Int($0) }                  // compactMap: преобразуем в Int, удаляя nil
    .map { $0 * 2 }                          // map: умножаем на 2
    .filter { $0 > 20 }                      // filter: оставляем только > 20
    
print(output) // [60]

Эволюция в Swift

Важно отметить эволюцию этих методов в Swift:

  • До Swift 4.0: flatMap использовался и для уплощения массивов, и для обработки опционалов
  • Swift 4.1+: flatMap только для уплощения, compactMap для обработки опционалов
  • Термин "filterMap" сейчас является неофициальным описанием функциональности compactMap, которая объединяет фильтрацию (filter) и преобразование (map)

Понимание различий между этими функциями позволяет писать более чистый, выразительный и безопасный код, соответствующий принципам функционального программирования в Swift.