4

I want to define a function .flatten that flattens several elements into one single array. I know that the following is not possible, but essentially I would like to do this:

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]

I get the following error:

TypeError: arguments.reduce is not a function

I understand that the above error is because arguments is only array-like, so it does not have the full capabilities of a true array. So there is the following, but I'm wondering if there is something even cleaner:

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

Any good way to rewrite .flatten using .reduce()?

NOTE: I know there are many other ways that you can flatten arrays in javascript, but what I was wondering about here is how to do so with specifically arguments.

  • `reduce` is not good for flattening arrays. Also, your code works. – 4castle Jul 23 '16 at 18:41
  • When you used `.call()` you forgot to pass an empty array as the second argument to `.reduce()`. – nnnnnn Jul 23 '16 at 18:44
  • Possible duplicate of [Merge/flatten an array of arrays in JavaScript?](http://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays-in-javascript) – 4castle Jul 23 '16 at 19:00
  • @4castle It is slightly different from that previous question, and these answers were still very helpful for me because my primary confusion was with how to specifically flatten *arguments* so that in the future I can use the same format (whether .reduce or any other function that operates on an array) – Aljosha Novakovic Jul 23 '16 at 19:09
  • @AljoshaNovakovic It's just that all of the answers to that question can also be used with `arguments`. All you have to do is use `Array.prototype` with whatever, and then `call` using `arguments`. – 4castle Jul 23 '16 at 19:11

5 Answers5

11

Convert the arguments object to an array first:

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

Or, use array methods on the arguments object directly:

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

In ES6, you can use Array.from() to convert any iterable or array-like object to an actual array:

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

Update in 2021 using modern language features:

function flatten(...args) {
    return args.flat();
}

And, you can pass a depth to .flat(n) (default is 1) if you want to flatten to a deeper level than just one level down.

Though, since we have these more modern features now built into the language, you probably can use them more directly rather than passing them to a function to work on them, but we'd have to see some particular use cases to make suggestions for inline ways to solve your problem rather than using this function.


FYI, there are lots of ways to flatten an array:

Merge/flatten an array of arrays

How to flatten nested array in javascript?

Flattening multidimensional Arrays in JavaScript

Array.prototype.flat() method

jfriend00
  • 683,504
  • 96
  • 985
  • 979
1

Use Array.prototype.concat.apply([], arguments)

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

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

If that looks ugly, and you don't mind creating an unused empty array, you can use:

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

In ES6 though, you can get the best of both worlds:

[].concat(...arguments)
4castle
  • 32,613
  • 11
  • 69
  • 106
1

I hope my answer is helpful, I know this post was from 2016.

function flatten(...args){

    const values= args.reduce(function sumNumss(total,element){  
        return `${total} ${element}`
    });
    
    return values
}

flatten('sdfe',[1,23,1,23],'sedfe')

using rest of (...args) syntax for the argument should do the trick.

jrswgtr
  • 2,287
  • 8
  • 23
  • 49
0

You can also test if elements are subArraythis way :

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 ]
kevin ternet
  • 4,514
  • 2
  • 19
  • 27
  • thanks! In your example you passed in a single array (with sub-arrays), but I noticed this will work even if your argument has additional separate arguments afterwards. It flattens ALL, nice. – Aljosha Novakovic Jul 23 '16 at 19:03
  • This can only flatten one array. It doesn't quite fit in with the others, where any number of arrays can be passed to `flatten`. – 4castle Jul 23 '16 at 19:06
0

Something like this:

BTW: I think using push is better than concat, as this doesn't create a new array

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 ]
Venkata Raju
  • 4,866
  • 3
  • 27
  • 36
  • Declaring a function inside a function is bad practice. Every time `flatten` is called, it will have to recreate `flatten0` – 4castle Jul 23 '16 at 19:19
  • @4castle I don't think `flatten0` has to be recreated every time (creating it once should be fine, as it doesn't use any variables from the outer function). Is there any way to verify your statement ? – Venkata Raju Jul 23 '16 at 19:28
  • [This question](http://stackoverflow.com/q/10204420/5743988) explains it well. You will be at the mercy of the scripting engine whether it is optimized or not. A good solution though is to use an IIFE (as outlined in the 2nd & 3rd answer) – 4castle Jul 23 '16 at 19:30
  • @4castle Thank you for the link. After reading the answers i have mixed feelings :). So, keeping the answer intact. Let the OP choose. – Venkata Raju Jul 23 '16 at 19:38