Поиск в массиве
Ряд шкафчиков можно перестроить, но чаще к нему подходят с вопросом. Что лежит в первом шкафчике и в последнем? Есть ли где-нибудь нужная вещь и под каким она номером? Чтобы ответить, ряд не трогают, а только заглядывают в шкафчики.
Часть ответов вы уже умеете получать по индексу. Последний элемент лежит по индексу count - 1. Но у пустого массива count равен 0, и запись scores[scores.count - 1] просит элемент с индексом -1. Такого шкафчика нет, и программа аварийно завершится с Index out of range.
Для таких вопросов у массива есть готовые средства. Они не завершают программу аварийно даже на пустом ряду:
let scores = [7, 9, 4, 9]
print(scores.first ?? 0, scores.last ?? 0) // 7 9
print(scores.contains(9), scores.contains(5)) // true false
if let index = scores.firstIndex(of: 9) {
print(index) // 1
}
print(scores.max() ?? 0, scores.min() ?? 0) // 9 4Давайте разберём программу по строкам:
firstиlastдают первый и последний элемент:7и9. Ответ приходит в коробке-опционале, поэтому его открывают через?? 0.contains(9)отвечаетtrue: девятка в ряду есть. Пятёрки нет, иcontains(5)даётfalse.firstIndex(of: 9)находит первую девятку по индексу1. Коробка не пуста, поэтомуif letвыполняет тело, и программа печатает1.max()иmin()находят наибольший и наименьший элемент:9и4.
Массив scores объявлен через let, а программа всё равно компилируется: ни один из этих вопросов массив не меняет.
Четыре вопроса к массиву
first и last
first — первый элемент массива, last — последний. У строки они работают так же, только дают символ. Пишут их без скобок, как count.
contains
contains(9) проходит ряд и отвечает, есть ли в нём 9. Ответ — обычный Bool, и его можно сразу поставить в условие if. Если нужен не ответ «да или нет», а место элемента, берут firstIndex(of:).
firstIndex(of:)
firstIndex(of: 9) идёт по ряду с начала и возвращает индекс первой найденной девятки. Вторую девятку, по индексу 3, метод уже не ищет. Найденный индекс пригодится, чтобы заменить элемент через квадратные скобки или удалить его через remove(at:).
max() и min()
max() возвращает наибольший элемент, min() — наименьший. Их пишут с пустыми круглыми скобками, а first и last — без скобок.
Когда ответа нет
first, last, firstIndex(of:), max() и min() возвращают опционал. У пустого массива нет первого элемента, а искомого числа в ряду может не оказаться. Тогда вместо значения приходит пустая коробка — nil. Поэтому значение извлекают через ?? или if let, как в первой программе.
Здесь картинка со шкафчиками неточна. В раздевалке ряд стоит, даже когда все шкафчики пусты. А пустой массив — это ряд без единого шкафчика, и первого шкафчика в нём нет:
let empty = [Int]()
print(empty.first ?? 0, empty.max() ?? 0) // 0 0
print(scores.firstIndex(of: 5) == nil) // true- У
emptyнет ни первого элемента, ни наибольшего, и оба??подставляют0. - Пятёрки в
scoresнет, поэтомуfirstIndex(of: 5)даётnil, и сравнение сnilпечатаетtrue. С таким ответомif letиз первой программы пропустил бы тело.
contains опционал не возвращает: на вопрос «есть ли?» ответ есть всегда, true или false.
На собеседовании могут спросить, чем scores.first отличается от scores[0]. Ответ: на пустом массиве scores[0] аварийно завершает программу, а first возвращает nil.
Частая ошибка
Разброс оценок посчитали как разность наибольшей и наименьшей:
let spread = scores.max() - scores.min() // ✗error: binary operator '-' cannot be applied to two 'Int?' operandsПрограмма не компилируется. max() и min() возвращают Int?, а вычесть одну коробку из другой нельзя: любая из них может оказаться пустой. Сначала откройте обе коробки, например одним if let:
if let best = scores.max(), let worst = scores.min() {
print(best - worst) // 5
}Внутри тела best и worst — обычные числа 9 и 4, и разность 5 печатается без ошибок.
Главное
firstиlastдают элементы с краёв ряда, аmax()иmin()— наибольший и наименьший.containsотвечаетBool, аfirstIndex(of:)даёт индекс первого совпадения.first,last,firstIndex(of:),max()иmin()возвращают опционал; его извлекают через??илиif let.- Эти вопросы не меняют массив, поэтому работают и с массивом через
let.