1297

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", ...]
Samuel Liew
  • 68,352
  • 105
  • 140
  • 225
Andy
  • 15,873
  • 12
  • 40
  • 52
  • https://gist.github.com/Nishchit14/4c6a7349b3c778f7f97b912629a9f228 This link describe ES5 & ES6 flatten – Nishchit Nov 17 '16 at 06:42
  • 26
    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](https://jsperf.com/concat-vs-reduce-concat-vs-loop) where as the single concat or simple loop would do 1000 operations. – gman Jul 30 '17 at 15:45
  • 15
    Simply user spread operator `[].concat(...array)` – Oleg Dater Mar 30 '19 at 19:17
  • @gman how is reduce + concat solution O((N^2)/2)? https://stackoverflow.com/questions/52752666/time-complexity-for-recrusive-deep-flatten mentions a different complexity. – Usman Jun 19 '19 at 12:13
  • 9
    With the latest browsers that support [ES2019](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat): `array.flat(Infinity)` where `Infinity` is the maximum depth to flatten. – Timothy Gu Apr 06 '20 at 02:47

85 Answers85

2151

You can use concat to merge arrays:

var arrays = [
  ["$6"],
  ["$12"],
  ["$25"],
  ["$25"],
  ["$18"],
  ["$22"],
  ["$10"]
];
var merged = [].concat.apply([], arrays);

console.log(merged);

Using the apply method of concat will just take the second parameter as an array, so the last line is identical to this:

var merged2 = [].concat(["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]);

There is also the Array.prototype.flat() method (introduced in ES2019) which you could use to flatten the arrays, although it is only available in Node.js starting with version 11, and not at all in Internet Explorer.

const arrays = [
      ["$6"],
      ["$12"],
      ["$25"],
      ["$25"],
      ["$18"],
      ["$22"],
      ["$10"]
    ];
const merge3 = arrays.flat(1); //The depth level specifying how deep a nested array structure should be flattened. Defaults to 1.
console.log(merge3);
    
str
  • 33,096
  • 11
  • 88
  • 115
Gumbo
  • 594,236
  • 102
  • 740
  • 814
  • 11
    Note that `concat` does not modify the source array, so the `merged` array will remain empty after the call to `concat`. Better to say something like: `merged = merged.concat.apply(merged, arrays);` – Nate Jan 24 '13 at 21:12
  • 65
    `var merged = [].concat.apply([], arrays);` seems to work fine to get it on one line. edit: as Nikita's answer already shows. – Sean Mar 15 '13 at 16:37
  • 44
    Or `Array.prototype.concat.apply([], arrays)`. – danhbear Jan 16 '14 at 01:03
  • 31
    Note: this answer only flattens one level deep. For a recursive flatten, see the answer by @Trindaz. – Phrogz Feb 21 '14 at 14:01
  • 240
    Further to @Sean's comment: ES6 syntax makes this super concise: `var merged = [].concat(...arrays)` – Sethi Jul 08 '15 at 13:50
  • I don't understand the last edit by @rofrol. Now the second part of the answer doesn't make sense -- *so the last line is identical* can't be true, since the `merged` variable wouldn't be defined yet, and you would get `‌ReferenceError: merged is not defined`. – iled May 31 '16 at 11:52
  • 1
    Building on @Sethi's comment: `Array.prototype.concat(...arrays)`. This version works with Typescript's 2.3.0 `--strict` mode. Doesn't work with nested arrays (it's not recursive). – Frederik Aalund May 16 '17 at 08:59
  • 2
    'apply' will stack overflow on large inputs for some vms, like v8. It's really not meant for this use case. – user1009908 Mar 10 '18 at 04:22
  • 1
    as @Sethi mentioned, for ES6: `[].concat(...arrays)` also works in Typescript – Eugenio Apr 17 '18 at 17:42
  • `[].concat( ...arrays)` – mim Mar 21 '19 at 18:48
  • 1
    Array.prototype.flat is now official! And [supported in Chrome, Firefox, Opera, Safari](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)! – iuliu.net May 09 '19 at 07:10
  • @Gumbo your solution will result 4,9, 5, , , 6, 4,5,5,7, 65, 56 for [[[4,9],5,null,[],6,[4,[5,5],7],65],56] whereas it should return may be 4,9, 5, null, 6, 4,5,5,7, 65, 56 or may be 4,9, 5, 6, 4,5,5,7, 65, 56 – Usman Jun 19 '19 at 12:24
  • On advantage over flat(): as of Nov 23, 2019, this works in Edge, while the prettier flat() does not. – den232 Nov 23 '19 at 17:56
  • Can we use this method for any depth of arrays? It seems that this code runs only for 1-level nested array. – TopW3 Jan 14 '20 at 07:57
  • Out of curiosity, why in `var merged = [].concat.apply([], arrays);`, thisArg is set to an empty array? – user2361494 May 01 '21 at 11:59
552

Here's a short function that uses some of the newer JavaScript array methods to flatten an n-dimensional array.

function flatten(arr) {
  return arr.reduce(function (flat, toFlatten) {
    return flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
  }, []);
}

Usage:

flatten([[1, 2, 3], [4, 5]]); // [1, 2, 3, 4, 5]
flatten([[[1, [1.1]], 2, 3], [4, 5]]); // [1, 1.1, 2, 3, 4, 5]
Noah Freitas
  • 16,298
  • 9
  • 47
  • 65
  • 10
    What's the memory usage profile for this solution? Looks like it creates a lot of intermediate arrays during the tail recursion.... – JBRWilkinson Jul 28 '15 at 18:28
  • 2
    Why is there an empty array passed as an argument? The code breaks without it, but what does it do? – ayjay Nov 18 '15 at 00:37
  • 8
    @ayjay, it's the starting accumulator value for the reduce function, what [mdn](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) calls the initial value. In this case it's the value of `flat` in the first call to the anonymous function passed to `reduce`. If it is not specified, then the first call to `reduce` binds the first value out of the array to `flat`, which would eventually result in `1` being bound to `flat` in both the examples. `1.concat` is not a function. – Noah Freitas Nov 18 '15 at 02:32
  • Given the multi-level nested support of `reduce`, this is much more performant that the accepted answer for nowadays – Saba Ahang Nov 23 '15 at 21:11
  • It is a nice solution and in most cases I would happily use it, but performance might not be the best: If you have for an array structure that has at the deepest child level a lot of values, it will still go through those even though it doesn't change them, so e.g. in the questions example it will go through all the arrays, not just the parent array. Even though it doesn't need to flatten anything else except the first tier. I had (naturally) performance issues with array structure of 10000+ arrays inside 2 parent arrays. – Hachi Dec 17 '15 at 07:07
  • 29
    Or in a shorter, sexier form: `const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);` – Tsvetomir Tsonev Aug 08 '16 at 14:18
  • 14
    Riffing on @TsvetomirTsonev and Noah's solutions for arbitrary nesting: `const flatten = (arr) => arr.reduce((flat, next) => flat.concat(Array.isArray(next) ? flatten(next) : next), []);` – Will Aug 16 '17 at 14:24
  • Bad memory profile, too many intermediate arrays, this answer will be rejected in most first-tier company interviews. – George Katsanos Sep 26 '17 at 19:45
  • 2
    @GeorgeKatsanos We don't know that - yes, `concat` is supposed to create a new array, but the runtimes can optimize just like hey already do e.g. for lots of kinds of string operations. If they detect the array is immediately discarded, and they can even limit the detection to the `reduce` function specifically, they can reuse the same array internally. Fort your interviews, I would say anybody who thinks that the observable behavior of JS code always is exactly the same as what the runtime does internally should maybe not be rejected but definitely educated - this stuff may change any time. – Mörre Jul 14 '18 at 20:35
  • I like the idea here but this will break if any of the original array values are not arrays – Matthew Dec 20 '19 at 17:54
328

There is a confusingly hidden method, which constructs a new array without mutating the original one:

var oldArray = [[1],[2,3],[4]];
var newArray = Array.prototype.concat.apply([], oldArray);
console.log(newArray); // [ 1, 2, 3, 4 ]
Nikita Volkov
  • 41,289
  • 10
  • 85
  • 162
  • 11
    In CoffeeScript this is `[].concat([[1],[2,3],[4]]...)` – amoebe Jun 21 '13 at 18:49
  • 8
    @amoebe your answer gives `[[1],[2,3],[4]]` as a result. The solution that @Nikita gives is correct for CoffeeScript as well as JS. – Ryan Kennedy Jul 31 '13 at 17:22
  • 5
    Ahh, I found your error. You have an extra pair of square brackets in your notation, should be `[].concat([1],[2,3],[4],...)`. – Ryan Kennedy Aug 06 '13 at 15:27
  • 22
    I think I found your error, too. The `...` are actual code, not some ellipsis dots. – amoebe Feb 23 '14 at 20:01
  • 1
    I'm not really convinced that this is "performant" as I seem to have hit a stack overflow with this sort of call (on an array with 200K entries which are lines in a file). – Steven Lu Feb 26 '14 at 23:09
  • 3
    Using a library function doesn't mean there's any less imperative "mess". Just that the mess is hidden inside a library. Obviously using a library is better than rolling your own function, but claiming that this technique reduces "imperative mess" is pretty silly. – paldepind Mar 25 '14 at 12:47
  • 3
    @paldepind The suggested approach does not use any library, but the standard JS functions. It does not mutate any values, that's why it isn't imperative, but functional. So I don't understand what exactly you found silly about it. – Nikita Volkov Mar 25 '14 at 12:56
  • 1
    Yes, you use a standard JavaScript library function. But doing so doesn't make your code any more functional in the "functional programming" sense. The actual function implementation might be what you call an "imperative mess". It's just hidden away in a library. Java has a pretty extensive standard library – but using it doesn't make your Java code any less imperative nor more functional. That's why I find your statement silly. – paldepind Mar 25 '14 at 14:35
  • 1. No problem there, I already am. You are right that in that sense your solution is functional. I think I misunderstood your original point. 2. A library is still a library even though it's implemented in C. A huge part of Pythons standard library is implemented in C as well. That's just implementation details of the library and they're still just as much libraries. 3. True. 4. No, that's not what I'm saying. – paldepind Mar 26 '14 at 09:59
  • 1
    Rather do Array.concat.apply(null, [[1],[2,3],[4]]) – kungfooman Feb 03 '15 at 10:51
  • 3
    @lama12345 That doesn't work as far as I can tell. The closest I can make work is `Array.prototype.concat.apply([], [[1],[2,3],[4]])` – Thor84no May 01 '15 at 15:43
  • 2
    If you can use ES2015 you might also write it easier for the eye with array spreads: `[].concat(...[ [1],[2,3],[4] ])`. – Loilo Jan 27 '17 at 12:37
  • 2
    did not work with array [2, [3, [4, [5, [6, [7, [8]]]]]]] – pareshm Sep 15 '17 at 09:40
  • Very clever approach, I like it! Extremely useful if you have to flatten an array inside of an array, since using apply would presume you are passing in an array of parameters. Only bummer is if you have arrays inside your array that are more than two levels deep. – NYC Tech Engineer Apr 10 '18 at 05:14
  • I like this approach... here for n-dimensional arrays: `flat = (e) => Array.isArray(e)? [].concat.apply([], e.map(flat)) : e` – rellampec Jul 30 '18 at 23:00
225

It can be best done by javascript reduce function.

var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];

arrays = arrays.reduce(function(a, b){
     return a.concat(b);
}, []);

Or, with ES2015:

arrays = arrays.reduce((a, b) => a.concat(b), []);

js-fiddle

Mozilla docs

Daniel Wolf
  • 10,328
  • 9
  • 42
  • 73
user2668376
  • 2,952
  • 1
  • 11
  • 4
  • Actually it is done by `concat` not `reduce`, `reduce` is redundant here! – Ali Shakiba Jan 30 '15 at 07:05
  • 1
    @JohnS Actually reduce feeds onto concat a single (array) value per turn. Proof? take out reduce() and see if it works. reduce() here is an alternative to Function.prototype.apply() – André Werlang Feb 23 '15 at 14:55
  • 3
    I would have used reduce as well but one interesting thing I learned from this snippet is that **most of the time you don't need to pass a initialValue** :) – José F. Romaniello Aug 21 '15 at 19:44
  • In TypeScript: `var array = arrays.reduce((a, b) => a.concat(b));` – Vojta Nov 27 '15 at 11:08
  • This is what MDN recommends: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce#Flatten_an_array_of_arrays – schilippe Jan 29 '16 at 17:36
  • 2
    The problem with reduce is that the array cannot be empty, so you will need to add an extra validation. – calbertts Jul 05 '16 at 22:57
  • @Werlang No, `Function.prototype.a‌​pply` will cause a stack oveflow for (very) large arrays. So neither `apply` nor the spread operator are an alternative for `reduce` in this case. –  Aug 17 '16 at 12:37
  • 4
    @calbertts Just pass the initial value `[]` and no further validations are necessary. –  Aug 17 '16 at 12:38
  • 10
    Since you use ES6, you could also use [the spread-operator as array literal](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Spread_operator). `arrays.reduce((flatten, arr) => [...flatten, ...arr])` – Putzi San Feb 19 '18 at 14:51
154

There's a new native method called flat to do this exactly.

(As of late 2019, flat is now published in the ECMA 2019 standard, and core-js@3 (babel's library) includes it in their polyfill library)

const arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

// Flatten 2 levels deep
const arr3 = [2, 2, 5, [5, [5, [6]], 7]];
arr3.flat(2);
// [2, 2, 5, 5, 5, [6], 7];

// Flatten all levels
const arr4 = [2, 2, 5, [5, [5, [6]], 7]];
arr4.flat(Infinity);
// [2, 2, 5, 5, 5, 6, 7];
Alister
  • 21,405
  • 8
  • 36
  • 31
  • 4
    It's a shame this isn't even on the first page of answers. This feature is available in Chrome 69 and Firefox 62 (and Node 11 for those working in the backend) – Mattwmaster58 Oct 28 '18 at 04:30
  • And there’s https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap – gsc Dec 07 '18 at 10:22
  • 2
    -1; nope, this isn't part of [ECMAScript 2018](http://www.ecma-international.org/ecma-262/9.0/). It's still just a proposal that hasn't made it to any ECMAScript spec. – Mark Amery Jan 20 '19 at 15:22
  • 1
    I think now we can consider this .. because now it's part of standard (2019) .. can we revisit the performance part of this once ? – Yuvaraj Feb 16 '19 at 12:01
  • Seems it's not yet supported by any Microsoft browser though (at least at the time I write this comment) – Laurent S. Aug 22 '19 at 14:42
  • As of Nov 23, 2019, flat() works in chrome but not in edge. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat – den232 Nov 23 '19 at 17:38
  • Today, [non-Chromium Edge is the only major browser that doesn't support `flat()`](https://caniuse.com/#feat=mdn-javascript_builtins_array_flat). But that's not a big deal, Chromium Edge is already available so you can tell your users to upgrade to it, and if nothing else [the next Windows 10 update will include Chromium Edge by default](https://gadgets.ndtv.com/apps/news/microsoft-edge-chromium-browser-added-by-default-next-windows-10-20h2-update-2247538). – Donald Duck Jun 29 '20 at 20:51
  • Fwiw, [here's](https://stackoverflow.com/questions/53072385/array-prototype-flat-is-undefined-in-nodejs/63995108#63995108) an unoptimized `flat` polyfill outside of babel. And then `flatMap` polyfill can be `return Array.isArray(array) ? array.map(callback).flat() : array;`. If you [need `map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Browser_compatibility) as well, [MDN has a `map` poly](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill). Or just transpile your code like most folk in 2020. ;^D – ruffin Sep 22 '20 at 15:29
83

Most of the answers here don't work on huge (e.g. 200 000 elements) arrays, and even if they do, they're slow. polkovnikov.ph's answer has the best performance, but it doesn't work for deep flattening.

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

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

Examples

Huge arrays

flatten(Array(200000).fill([1]));

It handles huge arrays just fine. On my machine this code takes about 14 ms to execute.

Nested arrays

flatten(Array(2).fill(Array(2).fill(Array(2).fill([1]))));

It works with nested arrays. This code produces [1, 1, 1, 1, 1, 1, 1, 1].

Arrays with different levels of nesting

flatten([1, [1], [[1]]]);

It doesn't have any problems with flattening arrays like this one.

Michał Perłakowski
  • 70,955
  • 24
  • 137
  • 155
  • Except your huge array is pretty flat. This solution won't work for deeply nested arrays. No recursive solution will. In fact no browser but Safari has TCO right now, so no recursive algorithm will perform well. – nitely Mar 04 '17 at 16:01
  • @nitely But in what real-world situation would you have arrays with more than a few levels of nesting? – Michał Perłakowski Jun 06 '17 at 13:45
  • 3
    Usually, when the array is generated out of user generated content. – nitely Jun 06 '17 at 17:05
  • @0xcaff In Chrome it doesn't work at all with a 200 000-element array (you get `RangeError: Maximum call stack size exceeded`). For 20 000-element array it takes 2-5 milliseconds. – Michał Perłakowski Jun 18 '17 at 16:02
  • 3
    what's the O notation complexity of this? – George Katsanos Sep 22 '17 at 07:05
  • @MichałPerłakowski i just tested this https://stackoverflow.com/a/49895394/4948383 with huge array and it works. – Manoj May 01 '18 at 14:49
  • @MichałPerłakowski `But in what real-world situation would you have arrays with more than a few levels of nesting?` -> When you design grammars, their parsers/lexers can generate deeply nested arrays easily. – customcommander Jul 20 '20 at 19:37
62

Update: it turned out that this solution doesn't work with large arrays. It you're looking for a better, faster solution, check out this answer.


function flatten(arr) {
  return [].concat(...arr)
}

Is simply expands arr and passes it as arguments to concat(), which merges all the arrays into one. It's equivalent to [].concat.apply([], arr).

You can also try this for deep flattening:

function deepFlatten(arr) {
  return flatten(           // return shalowly flattened array
    arr.map(x=>             // with each x in array
      Array.isArray(x)      // is x an array?
        ? deepFlatten(x)    // if yes, return deeply flattened x
        : x                 // if no, return just x
    )
  )
}

See demo on JSBin.

References for ECMAScript 6 elements used in this answer:


Side note: methods like find() and arrow functions are not supported by all browsers, but it doesn't mean that you can't use these features right now. Just use Babel — it transforms ES6 code into ES5.

Community
  • 1
  • 1
Michał Perłakowski
  • 70,955
  • 24
  • 137
  • 155
  • Because almost all the replies here misuse `apply` in this way, I removed my comments from yours. I still think using `apply`/spread this way is bad advise, but since no one cares... –  Aug 17 '16 at 13:17
  • @LUH3417 It's not like that, I really appreciate your comments. It turned out you're right -- this solution indeed doesn't work with large arrays. I posted [another answer](http://stackoverflow.com/a/39000004/3853934) which works fine even with arrays of 200 000 elements. – Michał Perłakowski Aug 17 '16 at 14:55
  • If you are using ES6, you can reducer further to: `const flatten = arr => [].concat(...arr)` – Eruant Sep 14 '17 at 10:49
  • What do you mean "does not work with large arrays"? How large? What happens? – GEMI Feb 06 '18 at 08:12
  • 5
    @GEMI For example trying to flatten a 500000-element array using this method gives "RangeError: Maximum call stack size exceeded". – Michał Perłakowski Feb 06 '18 at 15:21
53

You can use Underscore:

var x = [[1], [2], [3, 4]];

_.flatten(x); // => [1, 2, 3, 4]
Todd Yandell
  • 14,348
  • 2
  • 48
  • 37
44

Generic procedures mean we don't have to rewrite complexity each time we need to utilize a specific behaviour.

concatMap (or flatMap) is exactly what we need in this situation.

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// your sample data
const data =
  [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]

console.log (flatten (data))

foresight

And yes, you guessed it correctly, it only flattens one level, which is exactly how it should work

Imagine some data set like this

// Player :: (String, Number) -> Player
const Player = (name,number) =>
  [ name, number ]

// team :: ( . Player) -> Team
const Team = (...players) =>
  players

// Game :: (Team, Team) -> Game
const Game = (teamA, teamB) =>
  [ teamA, teamB ]

// sample data
const teamA =
  Team (Player ('bob', 5), Player ('alice', 6))

const teamB =
  Team (Player ('ricky', 4), Player ('julian', 2))

const game =
  Game (teamA, teamB)

console.log (game)
// [ [ [ 'bob', 5 ], [ 'alice', 6 ] ],
//   [ [ 'ricky', 4 ], [ 'julian', 2 ] ] ]

Ok, now say we want to print a roster that shows all the players that will be participating in game

const gamePlayers = game =>
  flatten (game)

gamePlayers (game)
// => [ [ 'bob', 5 ], [ 'alice', 6 ], [ 'ricky', 4 ], [ 'julian', 2 ] ]

If our flatten procedure flattened nested arrays too, we'd end up with this garbage result …

const gamePlayers = game =>
  badGenericFlatten(game)

gamePlayers (game)
// => [ 'bob', 5, 'alice', 6, 'ricky', 4, 'julian', 2 ]

rollin' deep, baby

That's not to say sometimes you don't want to flatten nested arrays, too – only that shouldn't be the default behaviour.

We can make a deepFlatten procedure with ease …

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.map(f).reduce(concat, [])

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)

// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [0, [1, [2, [3, [4, 5], 6]]], [7, [8]], 9]

console.log (flatten (data))
// [ 0, 1, [ 2, [ 3, [ 4, 5 ], 6 ] ], 7, [ 8 ], 9 ]

console.log (deepFlatten (data))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

There. Now you have a tool for each job – one for squashing one level of nesting, flatten, and one for obliterating all nesting deepFlatten.

Maybe you can call it obliterate or nuke if you don't like the name deepFlatten.


Don't iterate twice !

Of course the above implementations are clever and concise, but using a .map followed by a call to .reduce means we're actually doing more iterations than necessary

Using a trusty combinator I'm calling mapReduce helps keep the iterations to a minium; it takes a mapping function m :: a -> b, a reducing function r :: (b,a) ->b and returns a new reducing function - this combinator is at the heart of transducers; if you're interested, I've written other answers about them

// mapReduce = (a -> b, (b,a) -> b, (b,a) -> b)
const mapReduce = (m,r) =>
  (acc,x) => r (acc, m (x))

// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = f => xs =>
  xs.reduce (mapReduce (f, concat), [])

// concat :: ([a],[a]) -> [a]
const concat = (xs,ys) =>
  xs.concat (ys)

// id :: a -> a
const id = x =>
  x

// flatten :: [[a]] -> [a]
const flatten =
  concatMap (id)
  
// deepFlatten :: [[a]] -> [a]
const deepFlatten =
  concatMap (x =>
    Array.isArray (x) ? deepFlatten (x) : x)

// your sample data
const data =
  [ [ [ 1, 2 ],
      [ 3, 4 ] ],
    [ [ 5, 6 ],
      [ 7, 8 ] ] ]

console.log (flatten (data))
// [ [ 1. 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]

console.log (deepFlatten (data))
// [ 1, 2, 3, 4, 5, 6, 7, 8 ]
Thank you
  • 107,507
  • 28
  • 191
  • 224
  • Frequently, when I see your replies I want to withdraw mine, because they have become worthless. Great answer! `concat` itself doesn't blow up the stack, only `...` and `apply` does (along with very large arrays). I didn't see it. I just feel terrible right now. –  Aug 18 '16 at 22:39
  • 1
    Please note that `concat` in Javascript has a different meaning than in Haskell. Haskell's `concat` (`[[a]] -> [a]`) would be called `flatten` in Javascript and is implemented as `foldr (++) []` (Javascript: `foldr(concat) ([])` assuming curried functions). Javascript's `concat` is a weird append (`(++)` in Haskell), which can handle both `[a] -> [a] -> [a]` and `a -> [a] -> [a]`. –  Feb 03 '17 at 09:36
  • I guess a better name were `flatMap`, because that is exactly what `concatMap` is: The `bind` instance of the `list` monad. `concatpMap` is implemented as `foldr ((++) . f) []`. Translated into Javascript: `const flatMap = f => foldr(comp(concat) (f)) ([])`. This is of course similar to your implementation without `comp`. –  Feb 03 '17 at 09:36
  • what's the complexity of that algorithm? – George Katsanos Sep 22 '17 at 07:08
33

To flatten an array of single element arrays, you don't need to import a library, a simple loop is both the simplest and most efficient solution :

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

To downvoters: please read the question, don't downvote because it doesn't suit your very different problem. This solution is both the fastest and simplest for the asked question.

Denys Séguret
  • 335,116
  • 73
  • 720
  • 697
  • 4
    I would say, "Don't look for things more cryptic." ^^ –  Jun 02 '12 at 19:03
  • 4
    I mean... when I see people advising to use a library for that... that's crazy... – Denys Séguret Jun 02 '12 at 19:05
  • 3
    Ahh, to use a library *just* for this would be silly... but if it's already available. –  Jun 02 '12 at 19:05
  • 1
    If it's available it's not crazy, but it only makes things less readable and slower... – Denys Séguret Jun 02 '12 at 19:06
  • 2
    (I'm not exactly sure how a `map`, `flatMap`, or `flatten` function would make it less readable or practically slower though. But +1 for this solution that makes me think of C.) –  Jun 02 '12 at 19:08
  • 9
    @dystroy The conditional part of the for loop isn't that readable. Or is it just me :D – Andreas Jun 02 '12 at 19:13
  • @Andreas It's not just you, I was trying to figure out what the arrow operator does. Iterating over an array backwards smells of a microoptimisation. – millimoose Jun 02 '12 at 19:40
  • It's a classical one avoiding to fetch length (or any other getter) more than once. But you may go in the other direction if you like it more, that's without importance in that case. – Denys Séguret Jun 02 '12 at 19:43
  • 1
    that loop is unnecessarily cryptic – gotofritz May 16 '13 at 22:56
  • 2
    "cryptic"... Seriously... Even the youngest "programmer" shouldn't find "cryptic" a reversed loop. – Denys Séguret May 17 '13 at 06:46
  • 4
    It doesn't really matter how cryptic it is. This code "flattens" this `['foo', ['bar']]` to `['f', 'bar']`. – jonschlinkert Feb 09 '14 at 00:06
  • 2
    Well. Of course. It's an answer to the question. What's not clear in *an array of arrays* ? This answer doesn't try to answer a more general question which woukd be less efficient... – Denys Séguret Feb 09 '14 at 00:18
  • 1
    indeed, you're correct. I was focused too much on the other examples - not explicitly on the wording. – jonschlinkert Feb 09 '14 at 00:34
  • Please refrain from personal attacks in comments. @OmShankar while the solution is not ideal, it works for the question verbatim, which makes it valid. If you dislike it, vote it down and optionally explain why, without the "meeting SO's standards" or "rejected in an interview". Those are not valid arguments to why an answer is bad. – Madara's Ghost May 03 '16 at 18:10
  • over-engineering doesn't always fit the bill. This answer shows off another technique on how to flatten simple arrays (like OPs example), which is sometimes all that is required. – rlemon May 03 '16 at 18:11
33

A solution for the more general case, when you may have some non-array elements in your array.

function flattenArrayOfArrays(a, r){
    if(!r){ r = []}
    for(var i=0; i<a.length; i++){
        if(a[i].constructor == Array){
            r.concat(flattenArrayOfArrays(a[i], r));
        }else{
            r.push(a[i]);
        }
    }
    return r;
}
aug
  • 9,482
  • 6
  • 63
  • 84
Trindaz
  • 14,751
  • 20
  • 74
  • 103
  • 4
    This approach was very effective in flattening the nested array form of result-sets you get from a [JsonPath query](https://code.google.com/p/jsonpath/wiki/Javascript). – kevinjansz Jun 14 '13 at 06:26
  • 1
    Added as a method of arrays: `Object.defineProperty(Array.prototype,'flatten',{value:function(r){for(var a=this,i=0,r=r||[];i – Phrogz Feb 21 '14 at 13:59
  • This will break if we manually pass in the second argument. For example, try this: `flattenArrayOfArrays (arr, 10)` or this `flattenArrayOfArrays(arr, [1,[3]]);` - those second arguments are added to the output. – Om Shankar Aug 07 '15 at 19:10
  • 2
    this answer is not complete! the result of the recursion is never assigned anywhere, so result of recursions are lost – r3wt May 31 '16 at 18:29
  • 2
    @r3wt went ahead and added a fix. What you need to do is make sure that the current array you are maintaining, `r`, will actually concatenate the results from the recursion. – aug Mar 28 '17 at 01:29
31

What about using reduce(callback[, initialValue]) method of JavaScript 1.8

list.reduce((p,n) => p.concat(n),[]);

Would do the job.

rab
  • 3,942
  • 1
  • 27
  • 40
30

Another ECMAScript 6 solution in functional style:

Declare a function:

const flatten = arr => arr.reduce(
  (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
);

and use it:

flatten( [1, [2,3], [4,[5,[6]]]] ) // -> [1,2,3,4,5,6]

 const flatten = arr => arr.reduce(
         (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
       );


console.log( flatten([1, [2,3], [4,[5],[6,[7,8,9],10],11],[12],13]) )

Consider also a native function Array.prototype.flat() (proposal for ES6) available in last releases of modern browsers. Thanks to @(Константин Ван) and @(Mark Amery) mentioned it in the comments.

The flat function has one parameter, specifying the expected depth of array nesting, which equals 1 by default.

[1, 2, [3, 4]].flat();                  // -> [1, 2, 3, 4]

[1, 2, [3, 4, [5, 6]]].flat();          // -> [1, 2, 3, 4, [5, 6]]

[1, 2, [3, 4, [5, 6]]].flat(2);         // -> [1, 2, 3, 4, 5, 6]

[1, 2, [3, 4, [5, 6]]].flat(Infinity);  // -> [1, 2, 3, 4, 5, 6]

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

console.log( arr.flat() );

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

console.log( arr.flat() );
console.log( arr.flat(1) );
console.log( arr.flat(2) );
console.log( arr.flat(Infinity) );
diziaq
  • 4,203
  • 12
  • 38
  • 55
  • 3
    This is nice and neat but I think you have done an ES6 overdose. There is no need for the outer function to be an arrow-function. I would stick with the arrow-function for the reduce callback but flatten itself ought to be a normal function. – Stephen Simpson Feb 23 '16 at 15:17
  • 3
    @StephenSimpson but is there a need for the outer function to be a *non*-arrow-function ? *"flatten itself ought to be a normal function"* – by "normal" you mean "non-arrow", but why? *Why* use an arrow function in the call to reduce then? Can you supply your line of reasoning? – Thank you Oct 03 '17 at 19:44
  • 1
    @naomik My reasoning is that it is unnecessary. It's mainly a matter of style; I should have much clearer in my comment. There is no major coding reason to use one or the other. However, the function is easier to see and read as non-arrow. The inner function is useful as an arrow function as it is more compact (and no context created of course). Arrow functions are great for creating compact easy to read function and avoiding this confusion. However, they can actually make it more difficult to read when a non-arrow would suffice. Others may disagree though! – Stephen Simpson Oct 04 '17 at 09:29
  • Getting a `RangeError: Maximum call stack size exceeded` – Matt Westlake Feb 07 '18 at 14:31
  • @Matt, please share the evnironment you use to reproduce the error – diziaq Feb 08 '18 at 04:21
  • @diziaq Sorry I don't have that code anymore. Ended up using the accepted answer after all. `[].concat.apply([], arrays);` – Matt Westlake Feb 08 '18 at 17:28
  • You know what else is ES6? [`.flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat)! – Константин Ван Nov 08 '18 at 02:01
  • -1; `flat` isn't part of [ES6](http://www.ecma-international.org/ecma-262/6.0/). It's not even part of [ES9](http://www.ecma-international.org/ecma-262/9.0/). It's a [proposal](https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flat), not yet part of the ECMAScript spec, and [not yet available in all major JavaScript environments](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat#Browser_compatibility). – Mark Amery Jan 20 '19 at 15:16
23
const common = arr.reduce((a, b) => [...a, ...b], [])
YairTawil
  • 2,762
  • 17
  • 19
20

You can also try the new Array.flat() method. It works in the following manner:

let arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]].flat()

console.log(arr);

The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the 1 layer of depth (i.e. arrays inside arrays)

If you want to also flatten out 3 dimensional or even higher dimensional arrays you simply call the flat method multiple times. For example (3 dimensions):

let arr = [1,2,[3,4,[5,6]]].flat().flat().flat();

console.log(arr);

Be careful!

Array.flat() method is relatively new. Older browsers like ie might not have implemented the method. If you want you code to work on all browsers you might have to transpile your JS to an older version. Check for MDN web docs for current browser compatibility.

kabirbaidhya
  • 2,581
  • 2
  • 29
  • 51
Willem van der Veen
  • 19,609
  • 11
  • 116
  • 113
  • 2
    to flat higher dimensional arrays you simply can call the flat method with `Infinity` argument. Like this: `arr.flat(Infinity)` – Mikhail Dec 04 '19 at 11:07
16

You can use Array.flat() with Infinity for any depth of nested array.

var arr = [ [1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2,3,4,[1,2,3,4]]], [[1,2,3,4], [1,2,[1,2,3]], [1,2,3,4,5,[1,2,3,4,[1,2,3,4]]]] ];

let flatten = arr.flat(Infinity)

console.log(flatten)

check here for browser compatibility

Code Maniac
  • 33,907
  • 4
  • 28
  • 50
14

Please note: When Function.prototype.apply ([].concat.apply([], arrays)) or the spread operator ([].concat(...arrays)) is used in order to flatten an array, both can cause stack overflows for large arrays, because every argument of a function is stored on the stack.

Here is a stack-safe implementation in functional style that weighs up the most important requirements against one another:

  • reusability
  • readability
  • conciseness
  • performance

// small, reusable auxiliary functions:

const foldl = f => acc => xs => xs.reduce(uncurry(f), acc); // aka reduce

const uncurry = f => (a, b) => f(a) (b);

const concat = xs => y => xs.concat(y);


// the actual function to flatten an array - a self-explanatory one-line:

const flatten = xs => foldl(concat) ([]) (xs);

// arbitrary array sizes (until the heap blows up :D)

const xs = [[1,2,3],[4,5,6],[7,8,9]];

console.log(flatten(xs));


// Deriving a recursive solution for deeply nested arrays is trivially now


// yet more small, reusable auxiliary functions:

const map = f => xs => xs.map(apply(f));

const apply = f => a => f(a);

const isArray = Array.isArray;


// the derived recursive function:

const flattenr = xs => flatten(map(x => isArray(x) ? flattenr(x) : x) (xs));

const ys = [1,[2,[3,[4,[5],6,],7],8],9];

console.log(flattenr(ys));

As soon as you get used to small arrow functions in curried form, function composition and higher order functions, this code reads like prose. Programming then merely consists of putting together small building blocks that always work as expected, because they don't contain any side effects.

  • 1
    Haha. Totally respect your answer, although reading functional programming like this is still like reading Japanese character by character to me (English speaker). – Tarwin Stroh-Spijer Nov 03 '16 at 20:55
  • 2
    If you find yourself implementing features of language A in language B not as a part of project with the sole goal of doing exactly this then someone somewhere had taken a wrong turn. Could it be you? Just going with `const flatten = (arr) => arr.reduce((a, b) => a.concat(b), []);` saves you visual garbage *and* explanation to your teammates why you need 3 extra functions *and* some function calls too. – Daerdemandt Feb 20 '17 at 16:29
  • 3
    @Daerdemandt But if you write it as separate functions, you will probably be able to reuse them in other code. – Michał Perłakowski Jun 18 '17 at 16:08
  • @MichałPerłakowski If you need to use them in several places then don't reinvent the wheel and choose a package from [these](https://github.com/stoeffel/awesome-fp-js) - documented and supported by other people. – Daerdemandt Jun 18 '17 at 17:52
12

ES6 One Line Flatten

See lodash flatten, underscore flatten (shallow true)

function flatten(arr) {
  return arr.reduce((acc, e) => acc.concat(e), []);
}

or

function flatten(arr) {
  return [].concat.apply([], arr);
}

Tested with

test('already flatted', () => {
  expect(flatten([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]);
});

test('flats first level', () => {
  expect(flatten([1, [2, [3, [4]], 5]])).toEqual([1, 2, [3, [4]], 5]);
});

ES6 One Line Deep Flatten

See lodash flattenDeep, underscore flatten

function flattenDeep(arr) {
  return arr.reduce((acc, e) => Array.isArray(e) ? acc.concat(flattenDeep(e)) : acc.concat(e), []);
}

Tested with

test('already flatted', () => {
  expect(flattenDeep([1, 2, 3, 4, 5])).toEqual([1, 2, 3, 4, 5]);
});

test('flats', () => {
  expect(flattenDeep([1, [2, [3, [4]], 5]])).toEqual([1, 2, 3, 4, 5]);
});
zurfyx
  • 23,843
  • 15
  • 103
  • 130
  • Your 2nd example is better written as `Array.prototype.concat.apply([], arr)` because you create an extra array just to get to the `concat` function. Runtimes may or may not optimize it away when they run it, but accessing the function on the prototype doesn't look any uglier than this already is in any case. – Mörre Jul 14 '18 at 20:58
11

Using the spread operator:

const input = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
const output = [].concat(...input);
console.log(output); // --> ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]
Catfish
  • 17,019
  • 47
  • 183
  • 323
9

A Haskellesque approach

function flatArray([x,...xs]){
  return x ? [...Array.isArray(x) ? flatArray(x) : [x], ...flatArray(xs)] : [];
}

var na = [[1,2],[3,[4,5]],[6,7,[[[8],9]]],10];
    fa = flatArray(na);
console.log(fa);
Redu
  • 19,106
  • 4
  • 44
  • 59
8

If you only have arrays with 1 string element:

[["$6"], ["$12"], ["$25"], ["$25"]].join(',').split(',');

will do the job. Bt that specifically matches your code example.

Florian Salihovic
  • 3,760
  • 2
  • 15
  • 23
  • 3
    Whoever down voted, please explain why. I was searching for a decent solution and of all the solutions I liked this one the most. – Anonymous Apr 18 '14 at 22:34
  • 2
    @Anonymous I didn't downvote it since it technically meets the requirements of the question, but it's likely because this is a pretty poor solution that isn't useful in the general case. Considering how many better solutions there are here, I'd never recommend someone go with this one as it breaks the moment you have more than one element, or when they're not strings. – Thor84no May 01 '15 at 15:53
  • 2
    I love this solution =) – Huei Tan Aug 10 '15 at 10:01
  • 2
    It doesn't handle just arrays with 1 string elements, it also handles this array `['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')` – alucic Jan 25 '16 at 01:17
  • I discovered this method on my own, however knew it must have already been documented somewhere, my search ended here. The drawback with this solution is, it coerces numbers, booleans etc to strings, try `[1,4, [45, 't', ['e3', 6]]].toString().split(',')` ---- or ----- `[1,4, [45, 't', ['e3', 6], false]].toString().split(',')` – Sudhansu Choudhary Jul 16 '19 at 14:42
8

ES6 way:

const flatten = arr => arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flatten(next) : next), [])

const a = [1, [2, [3, [4, [5]]]]]
console.log(flatten(a))

ES5 way for flatten function with ES3 fallback for N-times nested arrays:

var flatten = (function() {
  if (!!Array.prototype.reduce && !!Array.isArray) {
    return function(array) {
      return array.reduce(function(prev, next) {
        return prev.concat(Array.isArray(next) ? flatten(next) : next);
      }, []);
    };
  } else {
    return function(array) {
      var arr = [];
      var i = 0;
      var len = array.length;
      var target;

      for (; i < len; i++) {
        target = array[i];
        arr = arr.concat(
          (Object.prototype.toString.call(target) === '[object Array]') ? flatten(target) : target
        );
      }

      return arr;
    };
  }
}());

var a = [1, [2, [3, [4, [5]]]]];
console.log(flatten(a));
Artem Gavrysh
  • 126
  • 1
  • 3
7
var arrays = [["a"], ["b", "c"]];
Array.prototype.concat.apply([], arrays);

// gives ["a", "b", "c"]

(I'm just writing this as a separate answer, based on comment of @danhbear.)

VasiliNovikov
  • 7,845
  • 2
  • 38
  • 49
7

I recommend a space-efficient generator function:

function* flatten(arr) {
  if (!Array.isArray(arr)) yield arr;
  else for (let el of arr) yield* flatten(el);
}

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

If desired, create an array of flattened values as follows:

let flattened = [...flatten([1,[2,[3,[4]]]])]; // [1, 2, 3, 4]
le_m
  • 15,910
  • 7
  • 55
  • 65
  • I like this approach. Similar to https://stackoverflow.com/a/35073573/1175496 , but uses the spread operator `...` to iterate through the generator. – The Red Pea Oct 27 '18 at 14:49
6

I would rather transform the whole array, as-is, to a string, but unlike other answers, would do that using JSON.stringify and not use the toString() method, which produce an unwanted result.

With that JSON.stringify output, all that's left is to remove all brackets, wrap the result with start & ending brackets yet again, and serve the result with JSON.parse which brings the string back to "life".

  • Can handle infinite nested arrays without any speed costs.
  • Can rightly handle Array items which are strings containing commas.

var arr = ["abc",[[[6]]],["3,4"],"2"];

var s = "[" + JSON.stringify(arr).replace(/\[|]/g,'') +"]";
var flattened = JSON.parse(s);

console.log(flattened)

  • Only for multidimensional Array of Strings/Numbers (not Objects)
vsync
  • 87,559
  • 45
  • 247
  • 317
  • Your solution is incorrect. It will contain the comma when flattening inner arrays `["345", "2", "3,4", "2"]` instead of separating each of those values to separate indices – pizzarob Nov 01 '16 at 22:05
  • @realseanp - you misunderstood the value in that Array item. I intentionally put that comma as a value and not as an Array delimiter comma to emphasize the power of my solution above all others, which would output `"3,4"`. – vsync Nov 01 '16 at 23:20
  • 1
    I did misunderstand – pizzarob Nov 02 '16 at 00:20
  • that seems definitely the fastest solution I've seen for this; are you aware of any pitfalls @vsync (except the fact it looks a bit hacky of course - treating nested arrays as strings:D) – George Katsanos Sep 26 '17 at 19:58
  • @GeorgeKatsanos - This method will not work for array items (and nested items) which are not of Primitive value, for example an item which points to a DOM element – vsync Sep 27 '17 at 08:24
  • @vsync - I got it. – Shyam Feb 05 '19 at 12:25
5

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]);
}
Niko
  • 25,682
  • 7
  • 85
  • 108
5

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] 
Jai
  • 869
  • 9
  • 15
5

I have done it using recursion and closures

function flatten(arr) {

  var temp = [];

  function recursiveFlatten(arr) { 
    for(var i = 0; i < arr.length; i++) {
      if(Array.isArray(arr[i])) {
        recursiveFlatten(arr[i]);
      } else {
        temp.push(arr[i]);
      }
    }
  }
  recursiveFlatten(arr);
  return temp;
}
balajivijayan
  • 679
  • 7
  • 11
  • 1
    Simple and sweet, this answer works better than the accepted answer. It flattens deeply nested levels to, not just the first level – Om Shankar Aug 07 '15 at 18:58
  • 1
    AFAIK that is lexical scoping and not a closure – dashambles Feb 03 '16 at 20:49
  • @dashambles is correct - the difference is that if it was a closure you would return the inner function to the outside and when the outer function is finished you can still use the inner function to access its scope. Here the lifetime of the outer function is longer than that of the inner function so a "closure" is never created. – Mörre Jul 14 '18 at 20:26
5

I was goofing with ES6 Generators the other day and wrote this gist. Which contains...

function flatten(arrayOfArrays=[]){
  function* flatgen() {
    for( let item of arrayOfArrays ) {
      if ( Array.isArray( item )) {
        yield* flatten(item)
      } else {
        yield item
      }
    }
  }

  return [...flatgen()];
}

var flatArray = flatten([[1, [4]],[2],[3]]);
console.log(flatArray);

Basically I'm creating a generator that loops over the original input array, if it finds an array it uses the yield* operator in combination with recursion to continually flatten the internal arrays. If the item is not an array it just yields the single item. Then using the ES6 Spread operator (aka splat operator) I flatten out the generator into a new array instance.

I haven't tested the performance of this, but I figure it is a nice simple example of using generators and the yield* operator.

But again, I was just goofing so I'm sure there are more performant ways to do this.

ashwell
  • 204
  • 2
  • 13
  • 1
    Similar answer (using generator and yield delegation), but in [PHP format](https://stackoverflow.com/a/31119295/1175496) – The Red Pea Oct 27 '18 at 03:40
5

just the best solution without lodash

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))
Vlad Ankudinov
  • 1,754
  • 1
  • 9
  • 18
5

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]
Avadhut Thorat
  • 269
  • 4
  • 3
4

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));
}
4

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
    Please edit your answer to include some explanation. Code-only answers do very little to educate future SO readers. Your answer is in the moderation queue for being low-quality. – mickmackusa Apr 21 '17 at 16:39
  • You are a god hahaha best answer ever – Waldemar Neto Apr 25 '17 at 02:04
  • 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; }, []); ``` – Ady Ngom Sep 26 '17 at 12:14
4

if you use lodash, you can just use its flatten method: https://lodash.com/docs/4.17.14#flatten

The nice thing about lodash is that it also has methods to flatten the arrays:

i) recursively: https://lodash.com/docs/4.17.14#flattenDeep

ii) upto n levels of nesting: https://lodash.com/docs/4.17.14#flattenDepth

For example

const _ = require("lodash");
const pancake =  _.flatten(array)
Holly Cummins
  • 9,508
  • 3
  • 19
  • 24
tldr
  • 10,743
  • 11
  • 69
  • 111
3

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

Sapikelio
  • 2,456
  • 2
  • 14
  • 37
3
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
}
alejandro
  • 2,608
  • 1
  • 15
  • 24
  • 3
    **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. – RBT May 13 '17 at 09:33
  • 1
    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 – meteorzero May 14 '17 at 20:10
  • 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`. – luwes Jun 10 '17 at 17:56
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>());
}
Tim
  • 20,192
  • 2
  • 13
  • 23
3

I think array.flat(Infinity) is a perfect solution. But flat function is a relatively new function and may not run in older versions of browsers. We can use recursive function for solving this.

const arr = ["A", ["B", [["B11", "B12", ["B131", "B132"]], "B2"]], "C", ["D", "E", "F", ["G", "H", "I"]]]
const flatArray = (arr) => {
    const res = []
    for (const item of arr) {
        if (Array.isArray(item)) {
            const subRes = flatArray(item)
            res.push(...subRes)
        } else {
            res.push(item)
        }
    }

    return res
}

console.log(flatArray(arr))
TopW3
  • 1,070
  • 3
  • 11
3
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]
samehanwar
  • 1,739
  • 2
  • 9
  • 18
3

In Node environment, if your version is not support Array.prototype.flat but you use es6 or ts, then you can do like this

const arrays = [
  ["$6"],
  ["$12"],
  ["$25"],
  ["$25"],
  ["$18"],
  ["$22"],
  ["$10"]
];
const merged = [].concat(...arrays);

console.log(merged);
Pylon
  • 338
  • 2
  • 7
3

There is 2 way to flat your array

*1-First One You Can Use Array.prototype.flat Method

const veryDeep = [[1, [2, 2, [3, [4, [5, [6]]]]], 1]];

veryDeep.flat(Infinity)
 // [1, 2, 2, 3, 4, 5, 6, 1]
 /**  If you don't know the depth of the array, 
simply pass Infinity **\

Or You Can Put Argument If You Know How Much Nested Your Array Is

const twoLevelsDeep = [[1, [2, 2], 1]];
//depth = 1
twoLevelsDeep.flat();
[1, [2, 2], 1]

//depth = 2
twoLevelsDeep.flat(2);
// [1, 2, 2, 1]

enter image description here

*2-Second Way You Can Flat Your Array By Using Spread Operator In ES6 And Concat It Will Flat Your Array Regardless How Many Nested In It

 const multidimension = [, [, , ], , [, ]];

   [].concat(...multidimension); // This will return: [, , , , , , 
   ]

   const multidimension = [1, [2, 3, 4], 5, [6, 7]];

   [].concat(...multidimension); // This will return: [1, 2, 3, 4, 5, 6, 7]
Yazan Najjar
  • 551
  • 5
  • 6
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;
};
elclanrs
  • 85,039
  • 19
  • 126
  • 159
  • dumping code is not an answer..please explain the steps of this solution. it seems like a very elegant one, but also, complex. – vsync Dec 28 '16 at 12:28
2

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.

EaterOfCode
  • 2,056
  • 2
  • 18
  • 33
  • Just wondering why this is a hack? I think it is a smart and simple way to do it. – Tim Hong Mar 13 '14 at 19:41
  • 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(',')` :) – EaterOfCode Mar 14 '14 at 10:17
  • Thanks for the explanation. I have just experienced that yesterday. =) – Tim Hong Mar 15 '14 at 04:35
  • @TimHong haha I hope nothing broke – EaterOfCode Mar 17 '14 at 10:06
  • 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. – Tim Hong Mar 17 '14 at 23:00
  • What will happen if the value contains a 'COMMA' ?? – Sahan Jul 20 '15 at 10:13
  • just like you expect, if arr is `[[1,2],["3,4"]]` it'll end up with: `["1","2","3", "4"]` – EaterOfCode Jul 20 '15 at 13:02
  • @EaterOfCode - but that isn't what you would expect, which should be `["1","2","3,4"]` – vsync Nov 01 '16 at 21:23
  • Needless to say, numbers are converted to strings with this solution. But it also fails with empty arrays: `[1,[2,3], [], 4, [5]].toString().split(',');` will output `["1", "2", "3", "", "4", "5"]`. Assuming we are interested in numbers, one alternative would be: `[1,[2,3], [], 4, [5]].toString().split(',').filter((e) => e !== '').map(Number);`. – Nicolás Fantone Aug 17 '17 at 13:09
2

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 ]
  • That's pretty brilliant. It does fail at a certain nesting depth: `[[1, 2], [3, 4, 5], [6, [[7]]]]`. – Noah Freitas May 07 '15 at 01:54
  • 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));`. –  Aug 17 '16 at 12:59
2

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]
Flip
  • 45
  • 1
  • 2
2
/**
* 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]);
  }, []);
}
alejandro
  • 2,608
  • 1
  • 15
  • 24
  • 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 :) – Om Shankar Jul 22 '18 at 21:08
  • 1
    I mean: `[1,2].concat(3)` is same as `[1,2].concat([3])` – Om Shankar Jul 22 '18 at 21:10
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

ashr
  • 249
  • 5
  • 11
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, "!"]
Alireza
  • 83,698
  • 19
  • 241
  • 152
  • This answer needs to be shown in a simpler fashion as a function. It's very hard to read and understand. – p32094 Mar 25 '19 at 21:07
2

Ways for making flatten array

  • using Es6 flat()
  • using Es6 reduce()
  • using recursion
  • using string manipulation

[1,[2,[3,[4,[5,[6,7],8],9],10]]] - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// using Es6 flat() 
let arr = [1,[2,[3,[4,[5,[6,7],8],9],10]]]
console.log(arr.flat(Infinity))

// using Es6 reduce()
let flatIt = (array) => array.reduce(
  (x, y) => x.concat(Array.isArray(y) ? flatIt(y) : y), []
)
console.log(flatIt(arr))

// using recursion
function myFlat(array) {
  let flat = [].concat(...array);
  return flat.some(Array.isArray) ? myFlat(flat) : flat;
}
console.log(myFlat(arr));

// using string manipulation
let strArr = arr.toString().split(','); 
for(let i=0;i<strArr.length;i++)
  strArr[i]=parseInt(strArr[i]);

console.log(strArr)
Community
  • 1
  • 1
akhtarvahid
  • 6,733
  • 2
  • 14
  • 21
2

This solution will work for any depth level (specifying how deep a nested array structure should be) of array.

function flatten(obj) {
    var out = [];
    function cleanElements(input) {
        for (var i  in input){
            if (input[i]instanceof Array){
                cleanElements(input[i]);
            }
            else {
                out.push(input[i]);
            }
        }
    }
    cleanElements(obj);
    return (out);
}
2

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));
Flavio Troia
  • 1,442
  • 1
  • 13
  • 25
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)
Dawit Mesfin
  • 127
  • 1
  • 1
  • 8
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

Artif3x
  • 3,205
  • 1
  • 21
  • 19
1

You can flatten an array of arrays using Array.prototype.reduce() and Array.prototype.concat()

var data = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]].reduce(function(a, b) {
  return a.concat(b);
}, []);
console.log(data);

Related docs: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

GibboK
  • 64,078
  • 128
  • 380
  • 620
1

I originally thought to use the .reduce method and recursively call a function to flatten inner arrays, however this can lead to stack overflows when you are working with a deeply nested array of deeply nested arrays. Using concat is also not the best way to go, because each iteration will create a new shallow copy of the array. What we can do instead is this:

const flatten = arr => {
    for(let i = 0; i < arr.length;) {
        const val = arr[i];
        if(Array.isArray(val)) {
            arr.splice(i, 1, ...val);
        } else {
            i ++;
        }
    }
    return arr;
}

We are not creating new arrays via concat and we are not recursively calling any functions.

http://jsbin.com/firiru/4/edit?js,console

pizzarob
  • 10,072
  • 4
  • 37
  • 63
  • is this technique really the "best"? better than [mine](http://stackoverflow.com/a/40369072/104380)? – vsync Nov 01 '16 at 21:38
  • sure it is. you're solution is incorrect because stringifying inner arrays will output a single string such as `"3, 4"` instead of `3` and `4` having separate indices. Also this solution can handle an index of any value. If the index is an array it will be flattened, if it is anything else it will remain as it is. – pizzarob Nov 01 '16 at 22:07
  • Not to mention this solution will elegantly handle deeply nested arrays. – pizzarob Nov 01 '16 at 22:16
  • why would you think mine won't handle infinite nested Array? what possibly made you say that? so in mine the whole point is **NOT** to break Array values which by themselves contain commas (which are part of the value) hence, my solution is still better (more readable and probably faster) – vsync Nov 01 '16 at 23:19
  • Sorry, I did misunderstand! Your solution is pretty good. The only thing is it cannot handle functions as a value at an index and this solution can, so I would have to say I would favor this. – pizzarob Nov 02 '16 at 00:21
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);
sylowgreen
  • 229
  • 1
  • 3
  • 4
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 ]
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>
Morris S
  • 1,457
  • 12
  • 24
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
vdegenne
  • 8,291
  • 10
  • 65
  • 90
1

Simple flatten util i've written

const flatten = (arr, result = []) => {
    if (!Array.isArray(arr)){
        return [...result, arr];
    }
     arr.forEach((a) => {
         result = flatten(a, result)
    })

    return result
}

console.log(flatten([1,[2,3], [4,[5,6,[7,8]]]])) // [ 1, 2, 3, 4, 5, 6, 7, 8 ]
customcommander
  • 11,496
  • 3
  • 35
  • 53
StateLess
  • 4,806
  • 3
  • 19
  • 24
1

Define an array of arrays in javascript called foo and flatten that array of arrays into a single array using javascript's array concat builtin method:

const foo = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]] 
console.log({foo}); 

const bar = [].concat(...foo) 
console.log({bar});

Should print:

{ foo: 
   [ [ '$6' ],
     [ '$12' ],
     [ '$25' ],
     [ '$25' ],
     [ '$18' ],
     [ '$22' ],
     [ '$10' ] ] }
{ bar: [ '$6', '$12', '$25', '$25', '$18', '$22', '$10' ] }
Eric Leschinski
  • 123,728
  • 82
  • 382
  • 321
kvimalkr55
  • 43
  • 7
1

Since ES2019 has introduced flat and flatMap then flat any array can be done like this:

const myArr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]
const myArrFlat= myArr.flat(Infinity);
console.log(myArrFlat); // ["$6", "$12", "$25", ...]

Ref. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat

Przemek Struciński
  • 3,598
  • 1
  • 21
  • 17
1

Recursively calling the deepFlatten function so we can spread the inner array without using any external helper method is the way to go.

const innerArr = ['a', 'b'];
const multiDimArr = [[1, 2], 3, 4, [5, 6, innerArr], 9];

const deepFlatten = (arr) => {
  const flatList = [];
  arr.forEach(item => {
    Array.isArray(item)
     ? flatList.push(...deepFlatten(item)) // recursive call
     : flatList.push(item)
  });
  return flatList;
}
Bijay Timilsina
  • 627
  • 1
  • 9
  • 22
1

solve this problem using single line of code..

function unflatten(arr){
    return arr.reduce((prevProps, nextProps) => {
        return prevProps.concat(Array.isArray(nextProps) ? unflatten(nextProps) : nextProps)
    }, [])
}

https://codepen.io/jyotishman/pen/BayVeoQ

jyotishman saikia
  • 1,612
  • 1
  • 9
  • 8
1

Use this method to flat two arrays

arr1.concat(...arr2);
аlex dykyі
  • 4,154
  • 23
  • 36
0
[1,[2,3],[4,[5,6]]].reduce(function(p, c) {
    return p.concat(c instanceof Array ? 
                    c.reduce(arguments.callee, []) : 
                    [c]); 
}, []);
spiderlama
  • 1,305
  • 13
  • 10
0

Here is my version of it. It allows you to flatten a complicated object which could be used in more scenarios:

Input

var input = {
   a: 'asdf',
   b: [1,2,3],
   c: [[1,2],[3,4]],
   d: {subA: [1,2]}
}

Code

The function is like this:

function flatten (input, output) {

  if (isArray(input)) {
    for(var index = 0, length = input.length; index < length; index++){
      flatten(input[index], output);
    }
  }
  else if (isObject(input)) {
    for(var item in input){
      if(input.hasOwnProperty(item)){
        flatten(input[item], output);
      }
    }
  }
  else {
    return output.push(input);
  }
};

function isArray(obj) {
  return Array.isArray(obj) || obj.toString() === '[object Array]';
}

function isObject(obj) {
  return obj === Object(obj);
}

Usage

var output = []

flatten(input, output);

Output

["asdf", 1, 2, 3, 1, 2, 3, 4, 1, 2]

Tim Hong
  • 2,602
  • 17
  • 23
0

If you need to support IE8 and, therefore, can't use methods such as reduce or isArray, here is a possible solution. It is a verbose approach to help you to understand the recursive algorithm.

function flattenArray(a){

    var aFinal = [];

    (function recursiveArray(a){

        var i,
            iCount = a.length;

        if (Object.prototype.toString.call(a) === '[object Array]') {
            for (i = 0; i < iCount; i += 1){
                recursiveArray(a[i]);
            }
        } else {
            aFinal.push(a);
        }

    })(a);

    return aFinal;

}

var aMyArray = [6,3,4,[12,14,15,[23,24,25,[34,35],27,28],56],3,4];

var result = flattenArray(aMyArray);

console.log(result);
Fabio Nolasco
  • 5,624
  • 5
  • 32
  • 31
0

var arrays = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"], ["$0"], ["$15"],["$3"], ["$75"], ["$5"], ["$100"], ["$7"], ["$3"], ["$75"], ["$5"]];
var merged = [].concat.apply([], arrays);
alert(merged);
wsc
  • 896
  • 7
  • 10
0
Array.prototype.flatten = Array.prototype.flatten || function() {
    return [].reduce.call(this, function(flat, toFlatten) {
        return flat.concat(Array.isArray(toFlatten) ? toFlatten.flatten() : toFlatten);
    },[])
};
0

I didn't find here solution for large arrays when flattening is not deep. So, my version:

function flatten(arr){
    result = []
    for (e of arr)
        result.push(...e)
    return result
}
adsurbum
  • 2,757
  • 3
  • 17
  • 27
0

You can just keep on using Array.flat() method to achieve this even when array is a lot more nested.

[1,2,3,[2]].flat() 

is equivalent to

[1,2,3,[2]].flat(1)

so as your nesting increases you can keep on increasing number.

eg:

[1,[2,[3,[4]]]].flat(3) // [1, 2, 3, 4]

And if you are not sure about level of nesting you can just pass Infinity as parameter

[1,2,3,[2,[3,[3,[34],43],[34]]]].flat(Infinity) //[1, 2, 3, 2, 3, 3, 34, 43, 34]
Manoj
  • 1,101
  • 7
  • 9
  • 1
    This doesn't work if the array contains a string which contains a comma. For example, `[['test,test'], ['test']].join().split(',')` gives `['test', 'test', 'test']` instead of `['test,test', 'test']`. – Michał Perłakowski May 01 '18 at 15:19
  • I understand it but comma is just an separator. if you want you can use any other which is for sure not be part of json. or you can use combination of special characters as separator. that's logical right. any ways thanks for down voting @MichałPerłakowski – Manoj May 01 '18 at 18:13
  • I normally use some jibberish, a pattern that is impossible in the dataset. – Anil Maharjan May 09 '18 at 05:28
  • @MichałPerłakowski I hope this fits better for all Array type. I found it on twitter and looks worth updating here. – Manoj Jun 12 '19 at 03:50
0

try this method,

arr = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]]
concat_arr = arr.concat.apply([], arr)
console.log(concat_arr)
Mohideen bin Mohammed
  • 14,471
  • 7
  • 86
  • 95
0

For Scala users looking for a way to replicate Seq.flatten in Javascript, here is a pimp of Array:

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

which can be used this way:

[[12, 3, 5], [1], [], [3, 4]].flatten() // [12, 3, 5, 1, 3, 4]
Xavier Guihot
  • 32,132
  • 15
  • 193
  • 118
0

I answer this question just with ES6, assume the deep array is:

const deepArray = ['1',[['a'],['b']],[2],[[[['4',[3,'c']]]],[5]]];

If you know or guess the depth of your arrays is not more than a number like 7, use below code:

const flatArray = deepArray.flat(7);

But if you don't know the depth of your deep arrays or your JavaScript engine doesn't support flat like react-native JavaScriptCore, use below function that is used JavaScript reduce function:

 const deepFlatten = arr =>
         arr.reduce(
           (acc, val) =>
             Array.isArray(val) 
               ? acc.concat(deepFlatten(val)) 
               : acc.concat(val),
             []
         );

Both of methods return below result:

["1", "a", "b", 2, "4", 3, "c", 5]
AmerllicA
  • 15,720
  • 11
  • 72
  • 103
0

I have a simple solution without using in a special js function. (like reduce etc)

const input = [[0, 1], [2, 3], [4, 5]]
let flattened=[];

for (let i=0; i<input.length; ++i) {
    let current = input[i];
    for (let j=0; j<current.length; ++j)
        flattened.push(current[j]);
}
Shmulik
  • 144
  • 2
  • 10
0

Much simpler and straight-forward one; with option to deep flatten;

const flatReduce = (arr, deep) => {
    return arr.reduce((acc, cur) => {
        return acc.concat(Array.isArray(cur) && deep ? flatReduce(cur, deep) : cur);
    }, []);
};

console.log(flatReduce([1, 2, [3], [4, [5]]], false)); // =>  1,2,3,4,[5]
console.log(flatReduce([1, 2, [3], [4, [5, [6, 7, 8]]]], true)); // => 1,2,3,4,5,6,7,8
Jegan
  • 121
  • 7
0

ES-Next solutions

  1. Array.from / Set / Array.flat / Array.sort
// 1. remove duplicate item
// 2. flat array
let arr= [["$6", ["$18"]], ["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];

let result = Array.from(new Set(arr.flat(Infinity)));

console.log(`flat array with unique filter`, result);
// 

// 1. remove duplicate item
// 2. sort by Asc order (munber only)
// 3. flat array

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

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

let result = Array.from(new Set(arr.flat(Infinity))).map(Number).sort((a,b)=> a > b ? 1: -1);

console.log(`flat array with unique filter & Asc sort`, result);
//
  1. ...spread / Set / Array.flat / Array.sort
// 1. remove duplicate item
// 2. flat array
let arr= [["$6", ["$18"]], ["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];

let result = [...new Set(arr.flat(Infinity))];

console.log(`flat array with unique filter`, result);
// 

// 1. remove duplicate item
// 2. sort by Asc order(number only)
// 3. flat array

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

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

let result = [...new Set(arr.flat(Infinity))].map(Number).sort((a,b)=> a > b ? 1: -1);

console.log(`flat array with unique filter & Asc sort`, result);
// 

refs

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

customcommander
  • 11,496
  • 3
  • 35
  • 53
xgqfrms
  • 5,516
  • 1
  • 37
  • 42
0

Yet another approach using jQuery $.map() function. From jQuery documentation:

The function can return an array of values, which will be flattened into the full array.

var source = [["$6"], ["$12"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]];
var target = $.map(source, function(value) { return value; }); // ["$6", "$12", "$25", "$25", "$18", "$22", "$10"]
BorisSh
  • 441
  • 4
  • 3
0

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]
Dere Sagar
  • 1,043
  • 7
  • 7
-1

What about deep flatten & Object Oriented ?

[23, [34, 454], 12, 34].flatten();
// -->   [23,34, 454, 12, 34]

[23, [34, 454,[66,55]], 12, 34].flatten();

// -->  [23, 34, 454, [66,55], 12, 34]

DEEP Flatten :

[23, [34, 454,[66,55]], 12, 34].flatten(true);

// --> [23, 34, 454, 66, 55, 12, 34]

DEMO

CDN


If all array elements are Integer,Float,... or/and String , So just , do this trick :

var myarr=[1,[7,[9.2]],[3],90];
eval('myarr=['+myarr.toString()+']');
print(myarr);
// [1, 7, 9.2, 3, 90]

DEMO

ΔȺȾΔ
  • 21,571
  • 10
  • 52
  • 80
Abdennour TOUMI
  • 64,884
  • 28
  • 201
  • 207
  • 6
    The code in the demo **doesn't make sense**, unless explained. – Om Shankar Aug 11 '15 at 20:31
  • 4
    [FYI, extending the native Array's prototype is generally discouraged.](https://stackoverflow.com/questions/8859828/javascript-what-dangers-are-in-extending-array-prototype) –  Aug 20 '15 at 17:04
-2

I believe that the best way to do this would be something like this:

var flatten = function () {
  return [].slice.call(arguments).toString().split(',');
};
Ivan Ferrer
  • 397
  • 4
  • 9
-2

Sheer Magic of ES6

const flat = A => A.reduce((A, a) => Array.isArray(a) ? [...A, ...flat(a)] : [...A, a], []);
Sumer
  • 1,591
  • 15
  • 17
-3

Using code from there.

I would write:

myArray.enumerable().selectMany(function(x) { return x; }).array()
Michał Perłakowski
  • 70,955
  • 24
  • 137
  • 155
-3

I was just looking for the faster and easy solution to do this, why? because I get this as a one interview question and I got curious, so I made this:

function flattenArrayOfArrays(a, r){
    if(!r){ r = []}
    for(var i=0; i<a.length; i++){
        if(a[i].constructor == Array){
            flattenArrayOfArrays(a[i], r);
        }else{
            r.push(a[i]);
        }
    }
    return r;
}

var i = [[1,2,[3]],4,[2,3,4,[4,[5]]]], output;

// Start timing now
console.time("flatten");
output = new Array(JSON.stringify(i).replace(/[^\w\s,]/g,"")); 
output
// ... and stop.
console.timeEnd("flatten");

// Start timing now
console.time("flatten2");
output = [].concat.apply([], i)
output
// ... and stop.
console.timeEnd("flatten2");

// Start timing now
console.time("flatten3");
output = flattenArrayOfArrays(i)
output
// ... and stop.
console.timeEnd("flatten3");

I used the most popular answers here and my solution. I guess somebody will find this interesting. Cheers!

damianfabian
  • 1,671
  • 11
  • 13