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.
Top comments (28)
I had not and now I feel much more informed.
I had suggested a French API name as a joke, but MooTools actually translated their API calls to other languages, including French. 😂 See this thread.
This is comedy gold.
I'm all for adorable names.
One of my favorite Ruby methods is
Array.zip
.Ruby's entire language is adorable. I wish the Ruby community had a mascot like the Go Gopher.
Yeah, I heard Ruby learned a lot from Perl so followed the convention of making after a precious stone.
I'm sure if it was written today it would have had an adorable name and a mascot to go along :)
Stones can be cute
And Ruby is a pretty adorable name. I can think of two Ruby devs who named their daughters Ruby. I don't necessarily endorse this kind of commitment but I know it's a thing.
Yeah, I met Sean Griffin and his Ruby at rubyconf last year.
I also have a friend who's dog is named Ruby.
Not gofer level cute, but still adorable af
I want to share our logo from a ruby community we are from Oaxaca México
That is a really cool logo!
I'll have to visit someday :)
It would be great!
How is
Array.zip
adorable? It's a standard sugar method in a bunch of languages.I just liked the visual of two arrays getting zipped together, something like a real-life zipper.
Yes, it exists in other languages as well, Ruby is just the first one I came across it, and the one I primarily work with nowadays.
I haven't heard of or thought about MooTools for a very long time (thanks to jQuery).
Seems odd that they would rename a method based on an old library using.
One of the major design points of the web as a platform is that old sites shouldn't break. While MooTools isn't as popular these days, it was one of the more popular JavaScript libraries many years ago, and a lot of sites still use it.
The issue with MooTools is that it prefers the built-in method (if one exists) over its own method. If it always overwrote the native method, we wouldn't have issues like this. This is also why "ponyfills" never use the native implementation of a function, even if available.
MooTools was always better than jQuery, though. jqueryvsmootools.com/
I'm still not sure if they're serious...
They are.
Of course MooTools' usage in new projects is negligible nowadays, but it wasn't in the past. And there are quite some old sites that still use it.
MooTools had this terrible practice to extend native prototypes. I'm still surprised that it actually gained some favor, but the worst part is that it's been part of Joomla at the time, so it ended up in sites whose maintainers didn't always get to make an educated choice (like jQuery).
In the end, yes, there's a non-negligible amount of sites that would break. Should we let those sites break? Keep in mind that it's not always about abandoned websites, but maybe also sites of small companies or commercial activities that don't have the resources to update their sites.
But alas, we don't have data to back up that.
I still think that letting the TC39 settle for
includes
instead ofcontains
(because of MooTools, again!) was a slippery slope, and this new issue proved me right.Now, we do have alternative decent names (like
flat
orsquash
), assmoosh
wasn't really a serious proposal (very inappropriate from Ficarra, anyway). But I think the adage "We shouldn't break any site when introducing new features" is just an impossible task for TC39. It's reasonable to think that every new feature has a non-zero impact on some existing website/webapp.I'd like to see real-world data, though.
The problem is not with the TC39 per se. TC39 only reflects the consensus of major vendor; what the point of a standard that will never be implemented; e.g.
Array.prototype.values
which was turned off in chrome and firefox because it breaks some outdated CMS.It was mainly a joke I think, but people let it escalate.
I think it made people more aware of TC-39 though.
They already have t-shirts 😂
🙃
element instanceof Array
->Array.isArray(element)
, otherwise not iframe-friendly. Refs web.mit.edu/jwalden/www/isArray.htmlGood call.
I just took the first Stackoverflow result xD
This is getting more serious!
Insert Wat meme here
I hate to seem really really boring, but I agree.
OK, I found the right name... finally.
I really hope smoosh() and smooshMap() are jokes LOL