Dictionary — это неупорядоченная коллекция, которая хранит пары «ключ — значение»
До сих пор мы говорили о списках, как о наборе элементов, теперь поговорим о несколько ином виде хранилища – похожем на записную книгу.
В массиве (Array) у нас есть список элементов, для доступа к конкретному нужно знать его индекс.
Представьте, что у вас есть список номеров ваших женщин – Жены, дочери, мамы, тёти, бабушки и любовниц.
Довольно сложно понять, какой индекс кому соответствует, скажем – кто у вас под индексом 5 ? Можно и запутаться.
Словарь решает подобную проблему – у вас есть конкретное ключ-значение, например:
Телефонная книга содержит:
- "Батя" → "+7-912-345-67-89"
- "Папа" → "+7-915-678-90-12"
- "Отец" → "+7-903-123-45-67"
Каждый ключ в словаре уникален, а значения могут повторяться. Мы не сможем запить под ключом “Батя” несколько значений, если конечно не станем использовать массив, но даже в таком случае для “Бати” будет только один вариант массива.
Это значит, что для одинаковык ключей не может быть разных данных.
// Сравним уже известные вам Массив
let fruitsArray: [String] = [
"Яблоко 🍎",
"Банан 🍌",
"Клубничка 🍓"
]
// И Словарь
let fruitsDictionary: [String: String] = [
"apple": "Яблоко 🍎",
"banana": "Банан 🍌",
"strawberry": "Клубничка 🍓"
]
| Тема | Описание |
|---|---|
| Основы | Что такое Dictionary и чем он отличается от массива |
| Применение | Когда удобно использовать словарь вместо других коллекций |
| Создание | Как объявлять и инициализировать Dictionary |
| Добавление | Как добавлять новые пары (ключ-значение) в словарь |
| Получаем данные | Почему словари всегда возвращают опциональные значения |
| Удаление | Как удалять элементы из словаря |
| Доступ | Как получать значения по ключу |
| Перебор | Как проходить по всем элементам словаря |
| Изменение | Как обновлять значения |
| Сложные типы | Какие типы могут быть ключами |
Представьте, что у вас есть телефонная книга 📒:
Телефонная книга содержит:
- "Мама" 👩 → "+7-912-345-67-89"
- "Папа" 👨 → "+7-915-678-90-12"
- "Друг" 🧑 → "+7-903-123-45-67"
Главные правила такой книги:
Dictionary vs Array| Характеристика | Массив (Array) |
Словарь (Dictionary) |
|---|---|---|
| Доступ | Индекс (let item = array[2]) |
Ключ (let phone = dict["Мама"]) |
| Тип индекса/ключа | Только Int |
Любой Hashable тип (String, Int, и т.д.) |
| Дубликаты | ✅ Разрешены | ❌ Ключи уникальны, значения могут повторяться |
| Скорость поиска | 🐢 Медленная (O(n)) | ⚡ Мгновенная (O(1)) |
| Аналогия | Полка с книгами 📚 | Телефонная книга 📒 |
| Порядок элементов | ✅ Сохраняется | ❌ Не гарантируется |
Пример в коде:
// Массив — ищем по позиции
let playlist = ["Песня 1", "Песня 2", "Песня 3"]
print(playlist[1]) // "Песня 2" (знаем индекс)
// Словарь — ищем по ключу
let phoneBook = [
"Мама": "👩 +7-912-345-67-89",
"Папа": "👨 +7-915-678-90-12"
]
print(phoneBook["Мама"]!) // "👩 +7-912-345-67-89" (знаем имя)
Dictionary 👍| Ситуация | Пример | Почему Dictionary? |
|---|---|---|
| Поиск по идентификатору | Найти пользователя по ID |
Мгновенный доступ по ключу |
| Настройки / Конфиги | Параметры приложения | Удобно хранить пары "volume": 44 |
| Кэширование данных | Временное хранение результатов | Быстрый доступ по ключу |
| Подсчет частоты | Сколько раз встречается слово | Ключ — слово, значение — счетчик |
| API ответы | JSON данные |
Прямая аналогия со структурой JSON |
На первый взгляд, представленные выше примеры кажутся неочевидными.
Это связано с недостатком опыта, не волнуйтесь – по мере продвижения в мире программирования, вы прекрасно разберётесь в ситуациях, в которых без Словаря не обойтись.
// ❌ Плохо: используем массив для поиска по ID
var usersArray: [(id: Int, name: String)] = []
// Чтобы найти пользователя с id = 101, нужно перебрать весь массив
// ✅ Оптимально: используем словарь
var usersDict: [Int: String] = [
101: "Анна",
102: "Иван",
103: "Ольга"
]
let userName = usersDict[101] // Мгновенно! "Анна"
var emptyNumbers: [String: Double] = [:] // Пустой словарь [String: Double]
Ключ имеет формат Строка ("pi"), а хранимое по нему значение – Число с плавающей точкой (3.14)
// Строковые ключи и строковые значения
var capitals: [String: String] = [
"Россия": "Москва",
"Франция": "Париж",
"Япония": "Токио"
]
// Целочисленные ключи и строковые значения
var httpStatus: [Int: String] = [
200: "OK",
404: "Not Found",
500: "Internal Server Error"
]
// Без указания типа (Swift сам разберётся, не маленький уже)
let scores = [
"Анна": 95,
"Иван": 87,
"Ольга": 92
] // Тип: [String: Int]
Когда вы создаёте словарь с уже готовыми данными – Swift самостоятельно может определить тип данных, но если вы создаёте пустой словарь – вам нужно указать тип данных вручную
var capitals: [String: String] = [:]
let vs var// var — можно изменять
var mutableDict = ["a": 1, "b": 2]
mutableDict["c"] = 3 // ✅ Работает!
// let — нельзя изменять
let immutableDict = ["a": 1, "b": 2]
immutableDict["c"] = 3 // ❌ Ошибка!
Как получить значение из словаря ? – нужно знать ключ
let fruitsDictionary = [
"apple": "Яблоко 🍎",
"banana": "Банан 🍌",
"strawberry": "Клубничка 🍓"
]
// Простой доступ (возвращает опционал, так как, а вдруг элемента по ключу нет ?)
let strawberry = fruits["strawberry"] // Optional("Клубничка 🍓")
let mango = fruits["mango"] // nil (такого ключа нет)
// С развертыванием опционала (Убедимся, что такой фрукт точно есть)
if let fruit = fruits["banana"] {
print("Нашли: \(fruit)") // Нашли: Банан 🍌
}
// Это модный запись – запомните его
// Если у нас в словаре нет значения для ключа "pear" – давайте вернём грушу, это же логично
let pear = fruits["pear", default: "Груша 🍐"] // "Груша 🍐" (ключа нет, вернули default)
⚠️ Важно! Dictionary всегда возвращает опциональное значение, потому что ключа может не существовать!
var fruitsDictionary = [
"apple": "Яблоко 🍎",
"banana": "Банан 🍌",
"strawberry": "Клубничка 🍓"
]
fruitsDictionary["cherry"] = "Черешня 🍒" // Добавили ключ-значение для Черешни
fruitsDictionary["apple"] = "Тыблоко 🍏" // Было "Яблоко 🍎", стало "Тыблоко 🍏"
// Способ 1: присвоить nil
fruitsDictionary["banana"] = nil // Банана теперь нет
fruitsDictionary.removeAll() // [:]
let scores = [
"Анна": 95,
"Иван": 87,
"Петр": 78
]
Здесь – в качестве ключа для словаря мы использует имя, а в качестве значения – количество балло.
Вот такая запись позволяет нам пройтись прямо по паре ключ-значение:
for (name, score) in scores {
print("\(name): \(score) баллов")
}
// Анна: 95 баллов
// Иван: 87 баллов
// Петр: 78 баллов
for name in scores.keys {
print("Студент: \(name)")
}
// Студент Анна
// Студент Иван
// Студент Ольга
// Студент Петр
for score in scores.values {
print("Балл: \(score)")
}
// Балл 95
// Балл 87
// Балл 92
// Балл 78
Мы можем создать массив из ключей словаря или из его значений
let names = Array(scores.keys) // ["Анна", "Иван", "Ольга", "Петр"]
let allScores = Array(scores.values) // [95, 87, 92, 78]
var dict = ["a": 1, "b": 2, "c": 3]
| Свойство | Описание | Пример |
|---|---|---|
count |
Количество элементов | dict.count // 3 |
isEmpty |
Проверка на пустоту | dict.isEmpty // false |
keys |
Все ключи | dict.keys // [“a”, “b”, “c”] |
values |
Все значения | dict.values // [1, 2, 3] |
first |
Первый элемент (не гарантирован) | dict.first // Optional((“a”, 1)) |
HashableЧтобы тип мог быть ключом в словаре, он должен поддерживать протокол Hashable:
Это обязательное требование, запомните.
Int, String, Double, Bool)Hashableenum)В качестве ключа, мы можем использовать создануню нами структуру
struct Person: Hashable {
let id: Int
let name: String
}
var personAges: [Person: Int] = [:]
let person1 = Person(id: 1, name: "Анна")
let person2 = Person(id: 2, name: "Иван")
personAges[person1] = 25
personAges[person2] = 30
print(personAges[person1]) // Optional(25)
Давайте представим, что у нас есть массив, в котором хранятся Персоны
let persons: [Person] = [
Person(id: 0, name: "Василий"),
Person(id: 1, name: "Пётр"),
Person(id: 2, name: "Пётр"),
]
Перед нами поставили задачу – сгруппировать всех персон по имени – т.е. все Пети, должны быть собраны в одном месте, ровно как и все Василии и так далее. В таком случае нам идеально подойдёт Словарь.
В качестве ключа мы выберем имя персоны, а в качестве значения – массив, в котором будут храниться все персоны с таким именем.
/// Шаг 1. Создадим пустой словарь, где ключ – Строка, значение – массив Персон
var usersDictionary: [String: [Person]] = [:]
// Шаг 2. Наполним наш словарь данными
func createDickFromArray() {
/// Давайте пройдёмся по всем персонам
for person in persons {
/// Важный момент!
/// Если у нас в словаре уже есть массив из имён – работаем с ним
if var array = usersDictionary[person.name] {
/// Добавим тёзку к уже существующим
array.append(person)
/// Не забудем обновить наш словарь
usersDictionary[person.name] = array
} else {
/// По данному ключу, ещё нет массива
/// Создадим его! – это будет наша первая персона
usersDictionary[person.name] = [person]
}
}
}
| Действие | Код | Пояснение |
|---|---|---|
| Создать пустой словарь | var dict: [String: Int] = [:] |
Пустой словарь |
| С данными | let dict = ["a": 1, "b": 2] |
С двумя парами |
| Получить значение | dict["a"] |
Optional(1) |
| С значением по умолчанию | dict["c", default: 0] |
0 (если ключа нет) |
| Добавить/обновить | dict["c"] = 3 |
Добавляет или обновляет |
| Удалить | dict["a"] = nil |
Удаляет пару |
| Количество | dict.count |
Сколько элементов |
| Проверить пустоту | dict.isEmpty |
true, если пусто |
let dict = ["a": 1, "b": 2]
let value = dict["c"] + 5 // ❌ Ошибка! value — опционал. Мы не можем сложить Optional(10) + 5
// ✅ Надо так:
if let value = dict["c"] {
let result = value + 5 // В данном случае value – уже точно число, по значениею "c"
}
// Или так:
let result = (dict["c"] ?? 0) + 5
let dict = ["a": 1, "b": 2]
dict["c"] = 3 // ❌ Ошибка! let = неизменяемый
// ✅ Надо использовать var
var mutableDict = ["a": 1, "b": 2]
mutableDict["c"] = 3 // ✅ Работает!
let dict = ["a": 1, "b": 2, "c": 3, "d": 4]
// Не надейтесь, что элементы будут в порядке a, b, c, d!
for (key, value) in dict {
print("\(key): \(value)") // Может быть: c: 3, a: 1, d: 4, b: 2
}
| Нужно | Выбор | Почему |
|---|---|---|
| Доступ по ключу | Dictionary |
dict["ключ"] — мгновенно |
| Сохранить порядок | Array |
У массива есть индексы |
| Уникальные ключи | Dictionary |
Ключи всегда уникальны |
| Простой список | Array |
Не нужны ключи |
| Связанные данные | Dictionary |
Пары “ключ-значение” |
| Значение по индексу | Array |
array[2] — легко |
Что вернёт fruits["mango"], если такого ключа нет в словаре?
nilКак добавить новую пару в словарь?
dict.append("key": "value")dict["key"] = "value"dict.add("key", "value")Какой тип данных может быть ключом в словаре?
StringIntHashable типРешение можете присылать сюда