ChatGPT解决这个技术问题 Extra ChatGPT

Merge/flatten an array of arrays

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", ...]
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.
With the latest browsers that support ES2019: array.flat(Infinity) where Infinity is the maximum depth to flatten.
I'm glad they put a maximum depth.. :D

s
str

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


Or Array.prototype.concat.apply([], arrays).
Note: this answer only flattens one level deep. For a recursive flatten, see the answer by @Trindaz.
Further to @Sean's comment: ES6 syntax makes this super concise: var merged = [].concat(...arrays)
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).
'apply' will stack overflow on large inputs for some vms, like v8. It's really not meant for this use case.
N
Noah Freitas

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]

What's the memory usage profile for this solution? Looks like it creates a lot of intermediate arrays during the tail recursion....
Why is there an empty array passed as an argument? The code breaks without it, but what does it do?
@ayjay, it's the starting accumulator value for the reduce function, what mdn 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.
Or in a shorter, sexier form: const flatten = (arr) => arr.reduce((flat, next) => flat.concat(next), []);
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), []);
N
Nikita Volkov

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 ]


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).
If you can use ES2015 you might also write it easier for the eye with array spreads: [].concat(...[ [1],[2,3],[4] ]).
did not work with array [2, [3, [4, [5, [6, [7, [8]]]]]]]
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.
I like this approach... here for n-dimensional arrays: flat = (e) => Array.isArray(e)? [].concat.apply([], e.map(flat)) : e
D
Daniel Wolf

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


Since you use ES6, you could also use the spread-operator as array literal. arrays.reduce((flatten, arr) => [...flatten, ...arr])
A
Alister

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];

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)
-1; nope, this isn't part of ECMAScript 2018. It's still just a proposal that hasn't made it to any ECMAScript spec.
I think now we can consider this .. because now it's part of standard (2019) .. can we revisit the performance part of this once ?
Seems it's not yet supported by any Microsoft browser though (at least at the time I write this comment)
M
Michał Perłakowski

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.


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 But in what real-world situation would you have arrays with more than a few levels of nesting?
Usually, when the array is generated out of user generated content.
@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.
what's the O notation complexity of this?
C
Community

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:

Spread operator

Arrow functions

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.


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...
@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 which works fine even with arrays of 200 000 elements.
If you are using ES6, you can reducer further to: const flatten = arr => [].concat(...arr)
What do you mean "does not work with large arrays"? How large? What happens?
@GEMI For example trying to flatten a 500000-element array using this method gives "RangeError: Maximum call stack size exceeded".
T
Todd Yandell

You can use Underscore:

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

_.flatten(x); // => [1, 2, 3, 4]

1+ - You can also specify that you want a shallow flattened array by specifying true for the second argument.
M
Mulan

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 ]


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.
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].
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.
what's the complexity of that algorithm?
D
Denys Séguret

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.


It doesn't really matter how cryptic it is. This code "flattens" this ['foo', ['bar']] to ['f', 'bar'].
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...
indeed, you're correct. I was focused too much on the other examples - not explicitly on the wording.
R
Ry-

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){
            flattenArrayOfArrays(a[i], r);
        }else{
            r.push(a[i]);
        }
    }
    return r;
}

This approach was very effective in flattening the nested array form of result-sets you get from a JsonPath query.
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.
d
diziaq

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


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.
@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?
@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!
Getting a RangeError: Maximum call stack size exceeded
@Matt, please share the evnironment you use to reproduce the error
k
kabirbaidhya

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.


to flat higher dimensional arrays you simply can call the flat method with Infinity argument. Like this: arr.flat(Infinity)
r
rab

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

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

Would do the job.


Y
YairTawil
const common = arr.reduce((a, b) => [...a, ...b], [])

C
Code Maniac

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


佚名

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.


Haha. Totally respect your answer, although reading functional programming like this is still like reading Japanese character by character to me (English speaker).
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 But if you write it as separate functions, you will probably be able to reuse them in other code.
@MichałPerłakowski If you need to use them in several places then don't reinvent the wheel and choose a package from these - documented and supported by other people.
Awkward and slow.
z
zurfyx

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

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.
C
Catfish

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"]


R
Ravi Ashara

var array = [["$6"], ["$12","$16"], ["$25"], ["$25"], ["$18"], ["$22"], ["$10"]] var a = array.flat(Infinity); console.log(a);


F
Florian Salihovic

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.


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 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.
It doesn't handle just arrays with 1 string elements, it also handles this array ['$4', ["$6"], ["$12"], ["$25"], ["$25", "$33", ['$45']]].join(',').split(',')
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(',')
l
le_m

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]

I like this approach. Similar to stackoverflow.com/a/35073573/1175496 , but uses the spread operator ... to iterate through the generator.
R
Redu

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


b
balajivijayan

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

Simple and sweet, this answer works better than the accepted answer. It flattens deeply nested levels to, not just the first level
AFAIK that is lexical scoping and not a closure
@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.
A
Artem Gavrysh

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


H
Holly Cummins

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)

a
ashwell

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.


V
Vlad Ankudinov

just the best solution without lodash

let flatten = arr => [].concat.apply([], arr.map(item => Array.isArray(item) ? flatten(item) : item))

v
vsync

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)


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
@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".
I did misunderstand
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)
@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
T
TopW3

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))


Thanks just now an interviewer asked me the same question.