Как бы вы использовали .reduce() для аргументов вместо определенного массива или объекта?

Я хочу определить функцию .flatten, которая объединяет несколько элементов в один массив. Я знаю, что следующее невозможно, но по существу я хотел бы сделать это:

var flatten = function() {
   var flattened = arguments.reduce(function(acc, elem) { return acc.concat(elem) }, []);
   return flattened;
}; 

var arr = [[1,2,3], 4, 5, [6, 7]];
console.log(flatten(arr)) // => [1,2,3,4,5,6,7]

Я получаю следующую ошибку:

TypeError: arguments.reduce is not a function

Я понимаю, что приведенная выше ошибка связана с тем, что аргументы похожи только на массив, поэтому он не обладает всеми возможностями истинного массива. Итак, есть следующее, но мне интересно, есть ли что-то еще чище:

var flatten = function() {
  var flattened = [].reduce.call(arguments, function(acc, elem) { return acc.concat(elem) });
  return flattened;
};

Есть ли хороший способ переписать .flatten с помощью .reduce()?

ПРИМЕЧАНИЕ. Я знаю, что есть много других способов выравнивания массивов в javascript, но мне было интересно, как это сделать с конкретными аргументами.


person Aljosha Novakovic    schedule 23.07.2016    source источник
comment
reduce не подходит для выравнивания массивов. Кроме того, ваш код работает.   -  person 4castle    schedule 23.07.2016
comment
Когда вы использовали .call(), вы забыли передать пустой массив в качестве второго аргумента для .reduce().   -  person nnnnnn    schedule 23.07.2016
comment
Возможный дубликат слияния/выравнивания массива массивов в JavaScript?   -  person 4castle    schedule 23.07.2016
comment
@4castle Это немного отличается от предыдущего вопроса, и эти ответы по-прежнему были очень полезны для меня, потому что моя основная путаница заключалась в том, как специально сгладить аргументы, чтобы в будущем я мог использовать тот же формат ( будь то .reduce или любая другая функция, работающая с массивом)   -  person Aljosha Novakovic    schedule 23.07.2016
comment
@AljoshaNovakovic Просто все ответы на этот вопрос также можно использовать с arguments. Все, что вам нужно сделать, это использовать Array.prototype с чем угодно, а затем call с arguments.   -  person 4castle    schedule 23.07.2016


Ответы (4)


Сначала преобразуйте объект arguments в массив:

var flatten = function() {
   var args = Array.prototype.slice.call(arguments);
   var flattened = args.reduce(function(acc, elem) { return acc.concat(elem) }, []);
   return flattened;
};

Или используйте методы массива непосредственно для объекта arguments:

var flatten = function() {
   var flattened = Array.prototype.reduce.call(arguments, function(acc, elem) { return acc.concat(elem) }, []);
   return flattened;
};

В ES6 вы можете использовать Array.from() для преобразования любого итерируемого или массивообразного объекта в фактический массив:

var flatten = function() {
   var args = Array.from(arguments);
   var flattened = args.reduce(function(acc, elem) { return acc.concat(elem) }, []);
   return flattened;
};

К вашему сведению, существует множество способов сгладить массив:

Объединить/сгладить массив массивов в JavaScript?

Как сгладить вложенный массив в javascript?

Сведение многомерных массивов в JavaScript

person jfriend00    schedule 23.07.2016
comment
Спасибо! Это именно то, что я искал, я не знал, что аргументы могут быть полностью преобразованы в такой массив. - person Aljosha Novakovic; 23.07.2016

Используйте 1_

function flatten() {
    return Array.prototype.concat.apply([], arguments);  
}

console.log(flatten([1, 2], 3, 4, [5, 6]));

Если это выглядит некрасиво, и вы не возражаете против создания неиспользуемого пустого массива, вы можете использовать:

[].concat.apply([], arguments)

Однако в ES6 вы можете получить лучшее из обоих миров:

[].concat(...arguments)
person 4castle    schedule 23.07.2016
comment
Спасибо, выглядит очень чисто. Вы сказали, что если вы не возражаете против создания неиспользуемого пустого массива... просто интересно, есть ли у этого недостатки? - person Aljosha Novakovic; 23.07.2016
comment
@AljoshaNovakovic Это просто создаст дополнительную вещь для сборки мусора. - person 4castle; 23.07.2016

Вы также можете проверить, являются ли элементы subArray таким образом:

function flatten(arr){
   var res = [];
   arr.forEach(x => Array.isArray(x) ? x.forEach(y => res.push(y)) : res.push(x));
  return res;
}

console.log(flatten([[1,2,3], 4, 5, [6, 7]])); // [ 1, 2, 3, 4, 5, 6, 7 ]
person kevin ternet    schedule 23.07.2016
comment
Благодарность! В вашем примере вы передали один массив (с подмассивами), но я заметил, что это будет работать, даже если впоследствии ваш аргумент будет иметь дополнительные отдельные аргументы. Сглаживает ВСЕ, приятно. - person Aljosha Novakovic; 23.07.2016
comment
Это может сгладить только один массив. Он не совсем стыкуется с остальными, где в flatten можно передать любое количество массивов. - person 4castle; 23.07.2016

Что-то вроде этого:

Кстати: я думаю, что использование push лучше, чем concat, так как это не создает новый массив

function flatten() 
{
  return flatten0([], Array.from(arguments));

  function flatten0(res, arrToFlatten)
  { 
    for (let el of arrToFlatten)
      Array.isArray(el) ? flatten0(res, el) : res.push(el);
    return res;
  }
}; 

let arr = [1, 2, [3, 4], [[5, [6], [[7]]]]];
console.log(flatten(arr)) // [ 1, 2, 3, 4, 5, 6, 7 ]
person Venkata Raju    schedule 23.07.2016
comment
Объявление функции внутри функции — плохая практика. Каждый раз, когда вызывается flatten, ему придется заново создавать flatten0. - person 4castle; 23.07.2016
comment
@4castle Я не думаю, что flatten0 нужно воссоздавать каждый раз (создание его один раз должно быть в порядке, поскольку оно не использует какие-либо переменные из внешней функции). Есть ли способ проверить ваше утверждение? - person Venkata Raju; 23.07.2016
comment
Этот вопрос хорошо объясняет это. Вы будете во власти скриптового движка независимо от того, оптимизирован он или нет. Однако хорошим решением является использование IIFE (как указано во 2-м и 3-м ответах) - person 4castle; 23.07.2016
comment
@4castle Спасибо за ссылку. После прочтения ответов у меня смешанные чувства :). Итак, сохраняем ответ нетронутым. Пусть ОП выбирает. - person Venkata Raju; 23.07.2016