Smooshing JavaScript
Tags: coding, javascript
Published: 2018-03-11 19:04:44

---
title: Smooshing JavaScript
published: true
cover_image: https://thepracticaldev.s3.amazonaws.com/i/9hwmgapt1w99e66vgt5m.jpeg
tags: coding, javascript
---

_[Cover image by David on Flickr, cropped by me.](https://www.flickr.com/photos/davidcharns)_


As a JavaScript developer you probably heard about smoosh-gate. Some [TC39](https://github.com/tc39) people (who are responsible for designing the ECMAScript standard that defines the langauge behaviour of JavaScript) had the idea to [rename](https://github.com/tc39/proposal-flatMap/pull/56) `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](https://github.com/tc39/proposal-flatMap) 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.

```javascript
// 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](http://bluebirdjs.com/docs/getting-started.html) 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`.

```javascript
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:

```javascript
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`.

```javascript
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:

```javascript
// 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.