Подсчёт в словаре
Частая задача — посчитать, сколько раз встречается каждый элемент: слово в тексте или имя в журнале звонков. Для такого подсчёта снова пригодится телефонная книга.
Представьте журнал звонков: Анна, Борис, снова Анна. Чтобы узнать, кто сколько раз звонил, заведите книгу, где напротив имени записан не номер, а число звонков. Встретили имя в журнале — нашли его строку и прибавили единицу. Имени в книге ещё нет — вписали его с числом 1.
В Swift такая книга — словарь [String: Int]: ключом служит то, что считаем, а значением — счётчик. Здесь картинка с книгой неточна. На бумаге имя, которого нет в книге, вы считаете за ноль звонков. А в Swift поиск по отсутствующему ключу даёт nil, и прибавить к нему единицу нельзя.
Поиск с запасным значением
Для такого случая у словаря есть поиск с запасным значением. Запись counts[name, default: 0] читается так: «значение по ключу name, а если ключа нет — 0». Здесь default: — метка аргумента, а не ветка switch. Результат такого поиска — обычный Int, без вопросительного знака:
let counts = ["Anna": 2, "Boris": 1]
print(counts["Anna", default: 0]) // 2
print(counts["Gleb", default: 0]) // 0Давайте разберём программу по строкам:
- Первая строка создаёт словарь со счётчиками звонков: Анна звонила дважды, Борис — один раз.
- Ключ
"Anna"есть, и поиск даёт сохранённое значение2. - Ключа
"Gleb"нет, и поиск даёт запасное значение0.
При чтении такая запись работает как counts["Gleb"] ?? 0. Разница видна при записи: через default: в словарь можно записать новое значение, а через ?? нельзя.
Подсчёт в цикле
Запись callCount[name, default: 0] += 1 делает три шага:
- Находит значение по ключу, а если ключа нет, берёт
0. - Прибавляет к нему единицу.
- Записывает результат под тем же ключом. Если ключа не было, в словаре появляется новая пара.
Нажмите «Следующий шаг» и следите за словарём callCount после каждого выполнения строки 4. Виджет записывает пары в том порядке, в каком они появились, а настоящий словарь порядка не хранит.
1let calls = ["Anna", "Boris", "Anna"]2var callCount: [String: Int] = [:]3for name in calls {4 callCount[name, default: 0] += 15}6print(callCount["Anna", default: 0])7print(callCount["Gleb", default: 0])8print(callCount.count)
В журнале три звонка: Анна звонила дважды, Борис — один раз.
Обратите внимание на последние шаги: чтение с default: ключ не добавляет. После callCount["Gleb", default: 0] в словаре по-прежнему две пары. Пара появляется, только когда через default: что-то записывают, как в += 1.
«Посчитайте, сколько раз встречается каждое слово» — одна из самых частых задач на собеседовании. На Swift её решают словарём [String: Int] и циклом с counts[word, default: 0] += 1.
Частая ошибка
Счётчик увеличили без default::
var callCount: [String: Int] = [:]
callCount["Anna"] += 1 // ✗error: value of optional type 'Int?' must be unwrapped to a value of type 'Int'Программа не компилируется: callCount["Anna"] имеет тип Int?, а прибавить число через += можно к Int, но не к Int?. Ключа "Anna" ещё нет, и прибавлять единицу не к чему. Запасное значение решает задачу: callCount["Anna", default: 0] += 1.
Главное
dict[key, default: 0]даёт обычное значение, а+= 1через него записывает пару, если её не было.- Чтение с
default:словарь не меняет: пара появляется только при записи. - Без
default:поиск даётInt?, и+= 1к нему не скомпилируется.