Использование 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 — локальная установка не требуется.
Все уроки этого курса
- Основы KeyPath
- Использование KeyPaths в коллекциях
- Записываемые и ссылочные KeyPaths
- @dynamicMemberLookup