Перейти к содержимому
Теория · Словари и множества · урок 3 из 6

Подсчёт в словаре

Частая задача — посчитать, сколько раз встречается каждый элемент: слово в тексте или имя в журнале звонков. Для такого подсчёта снова пригодится телефонная книга.

Представьте журнал звонков: Анна, Борис, снова Анна. Чтобы узнать, кто сколько раз звонил, заведите книгу, где напротив имени записан не номер, а число звонков. Встретили имя в журнале — нашли его строку и прибавили единицу. Имени в книге ещё нет — вписали его с числом 1.

В Swift такая книга — словарь [String: Int]: ключом служит то, что считаем, а значением — счётчик. Здесь картинка с книгой неточна. На бумаге имя, которого нет в книге, вы считаете за ноль звонков. А в Swift поиск по отсутствующему ключу даёт nil, и прибавить к нему единицу нельзя.

Поиск с запасным значением

Для такого случая у словаря есть поиск с запасным значением. Запись counts[name, default: 0] читается так: «значение по ключу name, а если ключа нет — 0». Здесь default: — метка аргумента, а не ветка switch. Результат такого поиска — обычный Int, без вопросительного знака:

SWIFT
let counts = ["Anna": 2, "Boris": 1]
print(counts["Anna", default: 0]) // 2
print(counts["Gleb", default: 0]) // 0

Давайте разберём программу по строкам:

  1. Первая строка создаёт словарь со счётчиками звонков: Анна звонила дважды, Борис — один раз.
  2. Ключ "Anna" есть, и поиск даёт сохранённое значение 2.
  3. Ключа "Gleb" нет, и поиск даёт запасное значение 0.

При чтении такая запись работает как counts["Gleb"] ?? 0. Разница видна при записи: через default: в словарь можно записать новое значение, а через ?? нельзя.

Подсчёт в цикле

Запись callCount[name, default: 0] += 1 делает три шага:

  1. Находит значение по ключу, а если ключа нет, берёт 0.
  2. Прибавляет к нему единицу.
  3. Записывает результат под тем же ключом. Если ключа не было, в словаре появляется новая пара.

Нажмите «Следующий шаг» и следите за словарём callCount после каждого выполнения строки 4. Виджет записывает пары в том порядке, в каком они появились, а настоящий словарь порядка не хранит.

ВживуюПодсчёт звонковшаг 1 из 11 · рамок: 1

В журнале три звонка: Анна звонила дважды, Борис — один раз.

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

Обратите внимание на последние шаги: чтение с default: ключ не добавляет. После callCount["Gleb", default: 0] в словаре по-прежнему две пары. Пара появляется, только когда через default: что-то записывают, как в += 1.

«Посчитайте, сколько раз встречается каждое слово» — одна из самых частых задач на собеседовании. На Swift её решают словарём [String: Int] и циклом с counts[word, default: 0] += 1.

Частая ошибка

Счётчик увеличили без default::

SWIFT
var callCount: [String: Int] = [:]
callCount["Anna"] += 1 // ✗
TEXT
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 к нему не скомпилируется.