Pull to refresh

Comments 11

С одной стороны, очень хочется оставить едкий комментарий типа «когда будет статья про понимание Array.length?». С другой, я действительно нередко наблюдал, как начинающие погромисты тупят при знакомстве с reduce. В следующий раз попробую подвергнуть такого человека чтению этой статьи.

Ваш пример — отличный пример подмены понятий. Никаких сложностей переписать 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, если кто-то может посмотреть и сказать что я таки правильно посчитал — буду благодарен.


Вот: https://jsperf.com/flatten1

Если мы знаем максимальное количество вложенных массивов, с которыми нам предстоит работать (в примере их 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);
Дорогой дневник! Сегодня я узнал, что Array#concat принимает больше одного аргумента. Это знание изменит мою жизнь. Или нет, но всё равно прикольно.

В вашем подходе будет много лишних проходов по массиву в arr.some(Array.isArray). В рекурсивном варианте такого не случится

Быстрее, чем вариант из статьи (в Chrome и Edge), рекурсия — это тоже не бесплатно:
https://jsperf.com/flatten2

Но в Firefox — худший. И в целом не лучший, особенно в сравнении с flattenReduceArrayES5 от Fen1kz

Но, согласитесь, ни одной новой переменной, даже функции нет, простой однострочник, TheShock правильно подметил, что автор нас пытается ввести в заблуждение.
C римскими цифрами вместо i: XVIII, XIX, XX… Я всегда думал, что лучше писать как в математике, i, j, k и т.д., а тут вот какой пример страшный.
А можно использовать js «магию»:
const arr = [1, [2], [3, [[4]]]]
const result = arr.toString().split(',').map(el => parseInt(el));
console.log(result);
// [1, 2, 3, 4]

Sign up to leave a comment.

Articles