1579

I have a JavaScript array like:

[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

How would I go about merging the separate inner arrays into one like:

["$6", "$12", "$25", ...]
4
  • 33
    All of the solutions that use reduce + concat are O((N^2)/2) where as a accepted answer (just one call to concat) would be at most O(N*2) on a bad browser and O(N) on a good one. Also Denys solution is optimized for the actual question and upto 2x faster than the single concat. For the reduce folks it's fun to feel cool writing tiny code but for example if the array had 1000 one element subarrays all the reduce+concat solutions would be doing 500500 operations where as the single concat or simple loop would do 1000 operations. Commented Jul 30, 2017 at 15:45
  • 13
    With the latest browsers that support ES2019: array.flat(Infinity) where Infinity is the maximum depth to flatten. Commented Apr 6, 2020 at 2:47
  • I'm glad they put a maximum depth.. :D Commented Mar 14, 2022 at 14:52
  • Flatmap works like champ, try this. var d = [[1], [2], [3], [4,5,6]].flatMap(x => x); Commented Feb 28, 2024 at 15:45

91 Answers 91

5

You can use "join()" and "split()":

let arrs = [
  ["$6"],
  ["$12"],
  ["$25"],
  ["$25"],
  ["$18"],
  ["$22"],
  ["$10"]
];

let newArr = arrs.join(",").split(",");

console.log(newArr); // ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]

In addition, you can use "toString()" and "split()" as well:

let arrs = [
  ["$6"],
  ["$12"],
  ["$25"],
  ["$25"],
  ["$18"],
  ["$22"],
  ["$10"]
];

let newArr = arrs.toString().split(",");

console.log(newArr); // ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]

However, both two ways above don't work properly if string contains commas:

"join()" and "split()":

let arrs = [
  ["$,6"],
  ["$,12"],
  ["$2,5"],
  ["$2,5"],
  [",$18"],
  ["$22,"],
  ["$,1,0"]
];

let newArr = arrs.join(",").split(",");

console.log(newArr); 
// ["$", "6", "$", "12", "$2", "5", "$2", "5", "", "$18", "$22", "", "$", "1", "0"]

"toString()" and "split()":

let arrs = [
  ["$,6"],
  ["$,12"],
  ["$2,5"],
  ["$2,5"],
  [",$18"],
  ["$22,"],
  ["$,1,0"]
];

let newArr = arrs.toString().split(",");

console.log(newArr); 
// ["$", "6", "$", "12", "$2", "5", "$2", "5", "", "$18", "$22", "", "$", "1", "0"]

Sign up to request clarification or add additional context in comments.

2 Comments

This is an extremely problematic and error prone way to do this.
@Brad. But it's fast if you're sure your array won't contain commas
4

That's not hard, just iterate over the arrays and merge them:

var result = [], input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"]];

for (var i = 0; i < input.length; ++i) {
    result = result.concat(input[i]);
}

Comments

4

Here is the recursive way...

function flatten(arr){
    let newArray = [];
    for(let i=0; i< arr.length; i++){
        if(Array.isArray(arr[i])){
          newArray =  newArray.concat(flatten(arr[i]))
        }else{
          newArray.push(arr[i])
        }
    }
  return newArray; 
}

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

Comments

4
function flatten(input) {
  let result = [];
  
  function extractArrayElements(input) {
    for(let i = 0; i < input.length; i++){
      if(Array.isArray(input[i])){
        extractArrayElements(input[i]);
      }else{
        result.push(input[i]);
      }
    }
  }
  
  extractArrayElements(input);
  
  return result;
}


// let input = [1,2,3,[4,5,[44,7,8,9]]];
// console.log(flatten(input));

// output [1,2,3,4,5,6,7,8,9]

Comments

4

Here is the fastest solution in Typescript, which works also on arrays with multiple levels of nesting:

export function flatten<T>(input: Array<any>, output: Array<T> = []): Array<T> {
    for (const value of input) {
        Array.isArray(value) ? flatten(value, output) : output.push(value);
    }
    return output;
}

and than:

const result = flatten<MyModel>(await Promise.all(promises));

Comments

4
const arr = [1, 2, [3, 4]];

arr.reduce((acc, val) => acc.concat(val), []);

Comments

4

Well if your coding environment supports ES6 (ES2015), you need not write any recursive functions or use array methods like map, reduce etc.

A simple spread operator (...) will help you flatten an Array of arrays into a single Array

eg:

  const data = [[1, 2, 3], [4, 5],[2]]
  let res = []
  data.forEach(curSet=>{
      res = [...res,...curSet]
  })
  console.log(res) //[1, 2, 3, 4, 5, 2]

Comments

4

I am using this method to flat mixed arrays: (which seems easiest for me). Wrote it in longer version to explain steps.

function flattenArray(deepArray) {
    // check if Array
    if(!Array.isArray(deepArray)) throw new Error('Given data is not an Array')

    const flatArray = deepArray.flat() // flatten array
    const filteredArray = flatArray.filter(item => !!item) // filter by Boolean
    const uniqueArray = new Set(filteredArray) // filter by unique values
    
    return [...uniqueArray] // convert Set into Array
}

// shorter version:

const flattenArray = (deepArray) => [...new Set(deepArray.flat().filter(item=>!!item))]
flattenArray([4,'a', 'b', [3, 2, undefined, 1], [1, 4, null, 5]])) // 4,'a','b',3,2,1,5

Codesandbox link

Modern method

Use of [].flat(Infinity) method

const nestedArray = [1,[2,[3],[4,[5,[6,[7]]]]]]
const flatArray = nestedArray.flat(Infinity)
console.log(flatArray)

Comments

3

It looks like this looks like a job for RECURSION!

  • Handles multiple levels of nesting
  • Handles empty arrays and non array parameters
  • Has no mutation
  • Doesn't rely on modern browser features

Code:

var flatten = function(toFlatten) {
  var isArray = Object.prototype.toString.call(toFlatten) === '[object Array]';

  if (isArray && toFlatten.length > 0) {
    var head = toFlatten[0];
    var tail = toFlatten.slice(1);

    return flatten(head).concat(flatten(tail));
  } else {
    return [].concat(toFlatten);
  }
};

Usage:

flatten([1,[2,3],4,[[5,6],7]]);
// Result: [1, 2, 3, 4, 5, 6, 7] 

2 Comments

careful, flatten(new Array(15000).fill([1])) throws Uncaught RangeError: Maximum call stack size exceeded and freezed my devTools for 10 seconds
@pietrovismara, my test is around 1s.
3

I propose two short solutions without recursion. They are not optimal from a computational complexity point of view, but work fine in average cases:

let a = [1, [2, 3], [[4], 5, 6], 7, 8, [9, [[10]]]];

// Solution #1
while (a.find(x => Array.isArray(x)))
    a = a.reduce((x, y) => x.concat(y), []);

// Solution #2
let i = a.findIndex(x => Array.isArray(x));
while (i > -1)
{
    a.splice(i, 1, ...a[i]);
    i = a.findIndex(x => Array.isArray(x));
}

Comments

3

The logic here is to convert input array to string and remove all brackets([]) and parse output to array. I'm using ES6 template feature for this.

var x=[1, 2, [3, 4, [5, 6,[7], 9],12, [12, 14]]];

var y=JSON.parse(`[${JSON.stringify(x).replace(/\[|]/g,'')}]`);

console.log(y)

1 Comment

this is the fastest and cleverest way to do it. I was using this to avoid recursion and easily rebuild the array, but yours is 3% faster. Way to go :) const flatten = function (A) { return A .toString() .split(',') .reduce( (a,c) => { let i = parseFloat(c); c = (!Number.isNaN(i)) ? i : c; a.push(c); return a; }, []);
3

Here is a version in Typescript based on the answer by artif3x, with a bonus implementation of flatMap for Scala fans.

function flatten<T>(items: T[][]): T[] {
  return items.reduce((prev, next) => prev.concat(next), []);
}

function flatMap<T, U>(items: T[], f: (t: T) => U[]): U[] {
  return items.reduce((prev, next) => prev.concat(f(next)), new Array<U>());
}

Comments

2

Here's another deep flatten for modern browsers:

function flatten(xs) {
  xs = Array.prototype.concat.apply([], xs);
  return xs.some(Array.isArray) ? flatten(xs) : xs;
};

1 Comment

dumping code is not an answer..please explain the steps of this solution. it seems like a very elegant one, but also, complex.
2

Nowadays the best and easy way to do this is joining and spliting the array like this.

var multipleArrays = [["$6","$Demo"], ["$12",["Multi","Deep"]], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]]

var flattened = multipleArrays.join().split(",")

This solution works with multiple levels and is also oneliner.

DEMO

EDIT for ECMAScript 6

Since ECMAScript 6 has been standardized, you can change the operation [].concat.apply([], arrays); for [].concat(...arrays);

var flattened = [].concat(...input);

DEMO

EDIT Most Efficient solution

The most efficient way to solve the problem is using a loop. You can compare the "ops/sec" velocity here

var flattened=[];
for (var i=0; i<input.length; ++i) {
    var current = input[i];
    for (var j=0; j<current.length; ++j)
        flattened.push(current[j]);
} 

DEMO

Hope It Helps

1 Comment

It doesn't work when array contains strings which contain commas, for example: [[","]].join().split(",") doesn't give desired result.
2
const flatten = array => array.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []); 

Per request, Breaking down the one line is basically having this.

function flatten(array) {
  // reduce traverses the array and we return the result
  return array.reduce(function(acc, b) {
     // if is an array we use recursion to perform the same operations over the array we found 
     // else we just concat the element to the accumulator
     return acc.concat( Array.isArray(b) ? flatten(b) : b);
  }, []); // we initialize the accumulator on an empty array to collect all the elements
}

3 Comments

From review queue: May I request you to please add some more context around your answer. Code-only answers are difficult to understand. It will help the asker and future readers both if you can add more information in your post.
Your code is written in cutting-edge, shortest way but honestly It takes me more time to figure out what it is actually doing... not good for maintenance
Nice one! You can flatten array like objects too if you change Array.isArray(b) in to b.length and add a Array.from(array) after the return instead of array.
2

It's better to do it in a recursive way, so if still another array inside the other array, can be filtered easily...

const flattenArray = arr =>
  arr.reduce(
    (res, cur) =>
       !Array.isArray(cur) 
       ? res.concat(cur)
       : res.concat(flattenArray(cur)), []);

And you can call it like:

flattenArray([[["Alireza"], "Dezfoolian"], ["is a"], ["developer"], [[1, [2, 3], ["!"]]]);

and the result isas below:

["Alireza", "Dezfoolian", "is a", "developer", 1, 2, 3, "!"]

1 Comment

This answer needs to be shown in a simpler fashion as a function. It's very hard to read and understand.
2

Just to add to the great solutions. I used recursion to solve this.

            const flattenArray = () => {
                let result = [];
                return function flatten(arr) {
                    for (let i = 0; i < arr.length; i++) {
                        if (!Array.isArray(arr[i])) {
                            result.push(arr[i]);
                        } else {
                            flatten(arr[i])
                        }
                    }
                    return result;
                }
            }

Test results: https://codepen.io/ashermike/pen/mKZrWK

Comments

2

Simply using spread operator we can flatten in the following way.

var OriginalArray = [[5, 1], [6], [2], [8]];
var newArray = [];

for (let i = 0; i < OriginalArray.length; i++) {
  newArray.push(...OriginalArray[i]);
}

console.log(newArray)

Comments

2

I have just try to solve the problem without using any inbuild function.

var arr = [1, 3, 4, 65, [3, 5, 6, 9, [354, 5, 43, 54, 54, 6, [232, 323, 323]]]];
var result = [];

function getSingleArray(inArr) {
  for (var i = 0; i < inArr.length; i++) {
    if (typeof inArr[i] == "object") {
      getSingleArray(inArr[i]); // Calling Recursively
    } else {
      result.push(inArr[i]);
    }
  }
}

getSingleArray(arr);
console.log(result); // [1, 3, 4, 65, 3, 5, 6, 9, 354, 5, 43, 54, 54, 6, 232, 323, 323]

Comments

2

Here is the solution non recursive flatten deep using a stack.

    function flatten(input) {
        const stack = [...input];
        const res = [];
        while (stack.length) {
            const next = stack.pop();
            if (Array.isArray(next)) {
                stack.push(...next);
            } else {
                res.push(next);
            }
        }
        return res.reverse();
    }
    const arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
    flatten(arrays);

Comments

1
[1,[2,3],[4,[5,6]]].reduce(function(p, c) {
    return p.concat(c instanceof Array ? 
                    c.reduce(arguments.callee, []) : 
                    [c]); 
}, []);

Comments

1

if your array only consists out of integers or strings you can use this dirty hack:

var arr = [345,2,[34],2,[524,[5456]],[5456]];
var flat = arr.toString().split(',');

Works, in FF, IE and Chrome didn't test the other browsers yet.

9 Comments

Just wondering why this is a hack? I think it is a smart and simple way to do it.
IMO it's a hack because it abuses the .toString() function (.toString will call for me .toString recursively) which if I recall correctly returned previously "[object Array]" instead of a recursive arr.join(',') :)
Thanks for the explanation. I have just experienced that yesterday. =)
@TimHong haha I hope nothing broke
Hahaha. It was all good. I tested it before checking in anything. In the end, I wrote my own version of it. =) I pasted my answer here. (Should be on top now, since it is the newest answer.) Yeah, thanks for asking. Hahaha.
|
1

There's a much faster way of doing this than using the merge.concat.apply() method listed in the top answer, and by faster I mean more than several orders of magnitude faster. This assumes your environment has access to the ES5 Array methods.

var array2d = [
  ["foo", "bar"],
  ["baz", "biz"]
];
merged = array2d.reduce(function(prev, next) {
    return prev.concat(next);
});

Here's the jsperf link: http://jsperf.com/2-dimensional-array-merge

1 Comment

I don’t know what’s wrong with the JSPerf because the link is dead, but this is backwards. Repeated concat is not only slower, it’s asymptotically slower.
1

I'm aware that this is hacky, but the must succinct way I know of to flatten an array(of any depth!) of strings(without commas!) is to turn the array into a string and then split the string on commas:

var myArray =[["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];
var myFlatArray = myArray.toString().split(',');

myFlatArray;
// ["$6", "$12", "$25", "$25", "$18", "$22", "$10", "$0", "$15", "$3", "$75", "$5", "$100", "$7", "$3", "$75", "$5"]

This should work on any depth of nested arrays containing only strings and numbers(integers and floating points) with the caveat that numbers will be converted to strings in the process. This can be solved with a little mapping:

var myArray =[[[1,2],[3,4]],[[5,6],[7,8]],[[9,0]]];
var myFlatArray = myArray.toString().split(',').map(function(e) { return parseInt(e); });
myFlatArray;
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]

1 Comment

Doesn't quite work. See my comments on previous answer: stackoverflow.com/a/21551946/4470169
1

To flatten a two-dimensional array in one line:

[[1, 2], [3, 4, 5]].reduce(Function.prototype.apply.bind(Array.prototype.concat))
// => [ 1, 2, 3, 4, 5 ]

2 Comments

That's pretty brilliant. It does fail at a certain nesting depth: [[1, 2], [3, 4, 5], [6, [[7]]]].
Nice hack! You can shorten your code if you use the following auxiliary functions: const bindable = Function.bind.bind(Function.bind); const applicable = bindable(Function.apply); and then [[1, 2], [3, 4, 5]].reduce(applicable([].concat));.
1

Recursive version that works on all datatypes

 /*jshint esversion: 6 */

// nested array for testing
let nestedArray = ["firstlevel", 32, "alsofirst", ["secondlevel", 456,"thirdlevel", ["theinnerinner", 345, {firstName: "Donald", lastName: "Duck"}, "lastinner"]]];

// wrapper function to protect inner variable tempArray from global scope;
function flattenArray(arr) {

  let tempArray = [];

  function flatten(arr) {
    arr.forEach(function(element) {
      Array.isArray(element) ? flatten(element) : tempArray.push(element);     // ternary check that calls flatten() again if element is an array, hereby making flatten() recursive.
    });
  }

  // calling the inner flatten function, and then returning the temporary array
  flatten(arr);
  return tempArray;
}

// example usage:
let flatArray = flattenArray(nestedArray);

Comments

1

The following code will flatten deeply nested arrays:

/**
 * [Function to flatten deeply nested array]
 * @param  {[type]} arr          [The array to be flattened]
 * @param  {[type]} flattenedArr [The flattened array]
 * @return {[type]}              [The flattened array]
 */
function flattenDeepArray(arr, flattenedArr) {
  let length = arr.length;

  for(let i = 0; i < length; i++) {
    if(Array.isArray(arr[i])) {
      flattenDeepArray(arr[i], flattenedArr);
    } else {
      flattenedArr.push(arr[i]);
    }
  }

  return flattenedArr;
}

let arr = [1, 2, [3, 4, 5], [6, 7]];

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

arr = [1, 2, [3, 4], [5, 6, [7, 8, [9, 10]]]];

console.log(arr, '=>', flattenDeepArray(arr, [])); // [ 1, 2, [ 3, 4 ], [ 5, 6, [ 7, 8, [Object] ] ] ] '=>' [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

Comments

1

You can use Ramda JS flatten

var arr = [[1,2], [3], [4,5]];
var flattenedArray = R.flatten(arr); 

console.log(flattenedArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

Comments

1
let arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];
arr = arr.reduce((a, b) => a.concat(b)); // flattened

Comments

1
/**
* flatten an array first level
* @method flatten
* @param array {Array}
* @return {Array} flatten array
*/
function flatten(array) {
  return array.reduce((acc, current) => acc.concat(current), []);
}


/**
* flatten an array recursively
* @method flattenDeep
* @param array {Array}
* @return {Array} flatten array
*/
function flattenDeep(array) {
  return array.reduce((acc, current) => {
    return Array.isArray(current) ? acc.concat(flattenDeep(current)) : acc.concat([current]);
  }, []);
}

/**
* flatten an array recursively limited by depth
* @method flattenDepth
* @param array {Array}
* @return {Array} flatten array
*/
function flattenDepth(array, depth) {
  if (depth === 0) {
    return array;
  }
  return array.reduce((acc, current) => {
    return Array.isArray(current) ? acc.concat(flattenDepth(current, --depth)) : acc.concat([current]);
  }, []);
}

2 Comments

Why do you even have a ternary operator, if the values on left and right are doing the same thing in first solution: return Array.isArray(current) ? acc.concat(current) : acc.concat([current]);? Why even check for if the item is also an array, if it's just one level :)
I mean: [1,2].concat(3) is same as [1,2].concat([3])

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.