Comments 11
Ваш пример — отличный пример подмены понятий. Никаких сложностей переписать for на такую-же рекурсивную форму, как ваш reduce нет, и, соответственно, нет перечисленных проблем. Да еще и намеренно написан ужасный нечитаемый код. Нда.
И опять же, в первом случае у вас не создается сотен промежуточных массивов, что потребление памяти, что время выполнения меньше, даже если переписать на рекурсию (передавая var flat = [] как параметр).
Я как-то против вашего решения.
Если начал использовать функциональный подход, то становится трудно остановиться, а вы решились на reduce и тут же остановились на грязной функции
flatten у вас делает слишком много работы — она и самим array занимается, и лезет в его вещи и там шурует и объявляет ещё функции внутри и создает кучу всего.
Предлагаю такую функцию:
var flattenReduceArray = (result, item) => Array.isArray(item) ? item.reduce(flattenReduceArray, result) : [...result, item]
Читабельный ES5 вариант:
function flattenReduceArrayES5 (result, item) {
if (Array.isArray(item)) {
return item.reduce(flattenReduceArrayES5, result)
} else {
result.push(item);
return result;
}
}
Во-первых, не создается никаких функций и промежуточных массивов — все пишется в один массив
Во-вторых, функция занимается исключительно элементами массива и ничем больше.
В-третьих — она быстрее. Я тут ради эксперимента попробовал использовать jsperf, если кто-то может посмотреть и сказать что я таки правильно посчитал — буду благодарен.
Если мы знаем максимальное количество вложенных массивов, с которыми нам предстоит работать (в примере их 4), то нам вполне подойдёт цикл for для итерации по каждому элементу массива, а затем оператор if, чтобы проверить является ли этот элемент самим массивом, и так далее…
function flatten() { var flat = []; for (var i=0; i<arr.length; i++) { if (Array.isArray(arr[i])) { for (var ii=0; ii<arr[i].length; ii++) { if (Array.isArray(arr[i][ii])) { for (var iii=0; iii<arr[i][ii].length; iii++) { for (var iiii=0; iiii<arr[i][ii][iii].length; iiii++) { if (Array.isArray(arr[i][ii][iii])) { flat.push(arr[i][ii][iii][iiii]); } else { flat.push(arr[i][ii][iii]); } } } } else { flat.push(arr[i][ii]); } } } else { flat.push(arr[i]); } } } // [1, 2, 3, 4]
Слишком мало вложенности, хипстеры могут не клюнуть! Давайте вручную напишем цикл глубиной в 20! А еще лучше глубиной в 30 элементов!
Блин, автор, неужели вы считаете своих читателей такими идиотами, что скармливаете им такую ересь? Это уже просто оскорбительно!
пс. да, это перевод.
let arr = [1, [2], [3, [[4]]]];
while (arr.some(Array.isArray)) {
arr = [].concat(...arr);
}
console.log(arr);
В вашем подходе будет много лишних проходов по массиву в arr.some(Array.isArray)
. В рекурсивном варианте такого не случится
https://jsperf.com/flatten2
Но в Firefox — худший. И в целом не лучший, особенно в сравнении с flattenReduceArrayES5 от Fen1kz
Но, согласитесь, ни одной новой переменной, даже функции нет, простой однострочник, TheShock правильно подметил, что автор нас пытается ввести в заблуждение.
const arr = [1, [2], [3, [[4]]]]
const result = arr.toString().split(',').map(el => parseInt(el));
console.log(result);
// [1, 2, 3, 4]
Понимание Array.prototype.reduce() и рекурсии на примере яблочного пирога