0Pricing
Swift Academy · Урок

Использование KeyPaths в коллекциях

Сортируйте коллекции и применяйте к ним map по ключевому пути.

«Использование KeyPaths в коллекциях» — бесплатный урок Swift Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.

Ключевые пути раскрывают возможности коллекций

Операции над коллекциями, такие как map, sorted и filter, часто лишь читают одно свойство каждого элемента. Ключевые пути позволяют выразить это намерение без написания замыкания.

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

struct Person {
    let name: String
    let age: Int
}

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let names = people.map(\.name)
print(names)

map с сокращённой записью ключевого пути

Передача map(\.property) эквивалентна написанию замыкания, возвращающего это свойство, но получается короче и понятнее. Начальные обратная косая черта и точка позволяют компилятору вывести тип элемента.

Сравните две формы ниже: они дают одинаковый результат.

struct Person {
    let name: String
    let age: Int
}

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let closureForm = people.map { $0.age }
let keyPathForm = people.map(\.age)
print(closureForm)
print(keyPathForm)

Сравнение по ключевому пути в sorted(by:)

Чтобы сортировать по свойству, по-прежнему нужно написать небольшое замыкание сравнения, но для ясности свойство можно прочитать внутри него через ключевой путь.

Здесь мы сортируем людей от самых младших к самым старшим, сравнивая их значения age.

struct Person {
    let name: String
    let age: Int
}

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let path = \Person.age
let sorted = people.sorted { $0[keyPath: path] < $1[keyPath: path] }
print(sorted.map(\.name))

Универсальный помощник для сортировки

Можно написать обобщённый помощник, который сортирует любой массив по любому сравнимому свойству, принимая свойство в качестве аргумента-ключевого пути. Благодаря этому сортировка по полю превращается в одну строку кода во всём проекте.

Ограничение T: Comparable гарантирует, что свойство можно упорядочить.

func sorted<E, T: Comparable>(_ array: [E], by path: KeyPath<E, T>) -> [E] {
    array.sorted { $0[keyPath: path] < $1[keyPath: path] }
}

struct Person { let name: String; let age: Int }
let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
print(sorted(people, by: \.age).map(\.name))

Фильтрация с помощью ключевого пути

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

Здесь у каждой задачи есть флаг isDone, и мы фильтруем задачи с помощью сокращённой записи ключевого пути.

struct Task {
    let title: String
    let isDone: Bool
}

let tasks = [Task(title: "A", isDone: true), Task(title: "B", isDone: false)]
let finished = tasks.filter(\.isDone)
print(finished.map(\.title))

Фильтрация по свойству, не являющемуся логическим

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

Здесь мы оставляем только тех людей, чей город совпадает с заданным.

struct Person { let name: String; let city: String }

func filter<E, T: Equatable>(_ array: [E], _ path: KeyPath<E, T>, equals value: T) -> [E] {
    array.filter { $0[keyPath: path] == value }
}

let people = [Person(name: "Ana", city: "Oslo"), Person(name: "Ben", city: "Rome")]
print(filter(people, \.city, equals: "Oslo").map(\.name))

max и минимум по ключевому пути

Методы max(by:) и min(by:) принимают замыкание сравнения так же, как и sorted. Прочитайте свойство через ключевой путь, чтобы найти элемент с наибольшим или наименьшим значением.

Здесь мы находим самого старшего человека.

struct Person { let name: String; let age: Int }

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25), Person(name: "Cy", age: 41)]
let path = \Person.age
if let oldest = people.max(by: { $0[keyPath: path] < $1[keyPath: path] }) {
    print(oldest.name)
}

Суммирование свойства с помощью reduce

Можно объединить map(\.property) с reduce, чтобы просуммировать числовое поле. Сначала получите из каждого элемента нужное число, а затем сложите все числа.

Здесь мы суммируем все значения возраста в одной цепочке операций.

struct Person { let name: String; let age: Int }

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let totalAge = people.map(\.age).reduce(0, +)
print(totalAge)

Группировка по ключевому пути

Dictionary(grouping:by:) распределяет элементы по группам на основе результата замыкания. Прочитайте свойство через ключевой путь, чтобы группировать элементы по этому полю.

Здесь люди группируются по городу в словарь массивов.

struct Person { let name: String; let city: String }

let people = [
    Person(name: "Ana", city: "Oslo"),
    Person(name: "Ben", city: "Rome"),
    Person(name: "Cy", city: "Oslo")
]
let path = \Person.city
let byCity = Dictionary(grouping: people, by: { $0[keyPath: path] })
print(byCity["Oslo"]?.map(\.name) ?? [])

Сортировка по нескольким полям

Чтобы сортировать по одному полю, а совпадения разрешать с помощью другого, сначала сравните значения по основному ключевому пути, а если они равны, используйте вторичный.

Здесь мы сортируем по возрасту, а людей одного возраста — по имени.

struct Person { let name: String; let age: Int }

let people = [
    Person(name: "Zoe", age: 30),
    Person(name: "Ana", age: 30),
    Person(name: "Ben", age: 25)
]
let sorted = people.sorted {
    if $0[keyPath: \Person.age] != $1[keyPath: \Person.age] {
        return $0.age < $1.age
    }
    return $0.name < $1.name
}
print(sorted.map(\.name))

Почему здесь ключевые пути лучше замыканий

Использование ключевых путей в вызовах операций над коллекциями уменьшает объём шаблонного кода, исключает возможность опечатки в теле замыкания и явно показывает намерение: получить одно поле.

Для сложных преобразований замыкание по-прежнему остаётся правильным инструментом, но для простого чтения свойства наиболее понятен вариант \.property.

struct Product { let name: String; let price: Double }

let products = [Product(name: "Pen", price: 2), Product(name: "Pad", price: 5)]
let names = products.map(\.name)
let prices = products.map(\.price)
print(names)
print(prices)

Быстрая проверка: ключевой путь в map

Выберите краткую запись ключевого пути для извлечения одного свойства из каждого элемента.

Итоги: ключевые пути в коллекциях

Вы увидели, как ключевые пути делают код коллекций более компактным:

  • map(\.property) получает одно поле без замыкания.
  • filter(\.boolProperty) напрямую работает с полями логического типа; для остальных используйте замыкание с [keyPath:].
  • Сортировка, max и min читают свойство через ключевой путь внутри своих замыканий сравнения.
  • Обобщённые помощники, принимающие KeyPath, превращают сортировку и фильтрацию по полю в универсальные операции, записываемые одной строкой.

Далее Вы узнаете о доступных для записи ключевых путях, которые позволяют не только читать значения, но и задавать их.

struct Person { let name: String; let age: Int }

let people = [Person(name: "Ana", age: 30), Person(name: "Ben", age: 25)]
let youngestFirst = people.sorted { $0[keyPath: \Person.age] < $1[keyPath: \Person.age] }
print(youngestFirst.map(\.name))

Часто задаваемые вопросы

Урок «Использование KeyPaths в коллекциях» бесплатный?

Да — полный текст урока «Использование KeyPaths в коллекциях» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.

Чему я научусь в уроке «Использование KeyPaths в коллекциях»?

Сортируйте коллекции и применяйте к ним map по ключевому пути. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Swift Academy?

Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Использование KeyPaths в коллекциях»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Swift Academy?

Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы KeyPath
  2. Использование KeyPaths в коллекциях
  3. Записываемые и ссылочные KeyPaths
  4. @dynamicMemberLookup
← Назад к Swift Academy