Cover image by David on Flickr, cropped by me.

As a JavaScript developer you probably heard about smoosh-gate. Some TC39 people (who are responsible for designing the ECMAScript standard that defines the langauge behaviour of JavaScript) had the idea to rename flatten to smoosh and flatMap to smooshMap . The rational behind this being some websites who use MooTools would break if they don't. These methods are a proposal right now and not part of the standard.

Anyway, I don't know much about MooTools or standards, but I wanted to explain what these functions acutally do.

Why

Most of the time, when programming JavaScript, you are probably messing around with your most favorite functors, arrays and promises. Sometimes they are nested, but you don't care about this. You need an array of numbers and not an array of arrays of numbers or you need a JSON result from your server and not a promise of a promise of JSON result.

What

Promises already come with a then method, that flattens out nested promises.



// loadDataA returns a promise const dataAPromise = loadDataA (); // dataAPromise resolves to a result with an id that is used to load the next data // loadDataB returns a promise too const dataBPromise = dataAPromise . then ( result => loadDataB ( result . id )) // dataBPromise resolves to the resulting data // and not the the promise returned from the callback above. // It's flattened out right away so you only care about the result const upperCasePromise = dataBPromise . then ( result => result . someText . toUpperCase ()) // upperCasePromise resolves to the upper case text // returned by the previous callback. upperCasePromise . then ( upperCaseText => console . log ( upperCaseText ));

So there isn't much to do here. Some promise libraries like Bluebird come with seperate map and flatMap methods, but mostly you will use then and don't care too much about flattening here.

The solution for this problem in arrays was to add a flatten and flatMap method to arrays. The flatten method replaces every nested array in the array with the content of that nested element, it also removes empty elements.

This function could be written manually with the help of reduce .



const flatten = a => a . reduce ( ( newArray , element ) => element instanceof Array ? [... newArray , ... element ] : element !== undefined ? [... newArray , element ] : newArray , [] ); const a = [ 1 , [ 2 , 3 , 4 ], , 5 ]; flatten ( a ); // -> [1, 2, 3, 4, 5]

We reduce a to a newArray by adding every element to it, if this array is an istanceof Array we add every element of that element to the newArray . (The ... -operator will create a new array for either case instead adding to the existing array, but I think you get the point).

The imperative version could look like that:



function flatten ( a ) { let b = []; for ( let element of a ) { if ( element instanceof Array ) { for ( let subElement of element ) { b . push ( subElement ); } } else if ( element !== undefined ) { b . push ( element ); } } return b ; } const a = [ 1 , [ 2 , 3 , 4 ], , 5 ]; flatten ( a ); // -> [1, 2, 3, 4, 5]

The flatMap version of this is simply calling flatten on a new array that was emitted by a map .



const flatMap = ( f , a ) => flatten ( a . map ( f )); const a = [ 1 , 0 , 7 , - 3 ]; flatMap ( x => x != 0 ? 1 / x : undefined , a ); // -> [1, 0.14285714285714285, -0.3333333333333333] const c = [ 1 , 2 , 5 ]; flatMap ( x => '' . padEnd ( x , " . " ). split ( "" ), c ); // -> [".", ".", ".", ".", ".", ".", ".", "."]

The real implementations will probably work like methods and not functions:



// Instead of flatten ( a . map ( x => [ 1 , x ])); // it would be a . map ( x => [ 1 , x ]). flatten (); // instead of flatMap ( x => [ 1 , x ], a ); // it would be a . flatMap ( x => [ 1 , x ]);

Conclusion

Flatten is a rather important action performed many times in every program, so it would be nice if JavaScript came with a built-in version, independent of its final name, lol.