Можно ли объединить массивы через reduce
Что проверяют
Знаете ли вы стандартную библиотеку Swift и цену копирования массивов. Вопрос почти всегда продолжается: «А как сделать это эффективнее?» или «Чем reduce(into:) отличается от обычного reduce?».
Как отвечать
Да, можно, но это не лучший инструмент. Покажите три варианта по возрастанию качества:
arrays.reduce([], +): коротко и работает. Но каждый+создаёт новый массив и копирует в него всё накопленное, поэтому на большом количестве массивов время растёт квадратично;arrays.reduce(into: []) { $0.append(contentsOf: $1) }: аккумулятор передаётся какinoutи дописывается на месте, лишних копирований нет, итог линейный. Формаreduce(into:)появилась в Swift 4 как раз ради таких случаев;arrays.flatMap { $0 }илиArray(arrays.joined()): идиоматичный вариант, прямо говорит, что происходит.joined()сам по себе ленивый и возвращаетFlattenSequence, массив из него получается явнымArray(...).
Для двух массивов ничего этого не нужно: a + b или a.append(contentsOf: b).
Хорошо дополнить соседними задачами, которые решаются тем же reduce(into:):
- объединение словарей:
dicts.reduce(into: [:]) { $0.merge($1) { _, new in new } }, где замыкание решает, какое значение оставить при совпадении ключа; - объединение без дублей: через
Set, если порядок не важен, илиOrderedSetиз пакета swift-collections, если важен; - резервирование памяти: при известном итоговом размере
reserveCapacityперед дописыванием убирает лишние перевыделения буфера.
Где спотыкаются
Пишут пример на JavaScript с concat. Для iOS-собеседования это ответ не на тот вопрос, а сигнатура reduce в Swift другая.
Не видят квадратичной сложности в reduce([], +). На паре массивов разница незаметна, на тысяче уже видна в профилировщике, и именно об этом спросят следом.
Путают flatMap и compactMap. Вариант flatMap, убирающий nil из массива опционалов, устарел в Swift 4.1 и переименован в compactMap, а flatMap оставлен для разворачивания вложенных последовательностей.