exsertus.com
The ramblings of an IT Professional, Long Distance Runner, Creative, and Maker of Stuff.
Coding Shortcuts
Arrow functions, map/filter/sort
17th March 2020
 4 min read
Keywordsjavascript
Categoriessoftware

Contents


I've been migrating and refactoring some old projects of late and have discovered (sorry, a bit late to the party) a load of useful features that can make code a bit cleaner and more efficient.

I'm focusing specifically on JavaScript, and may write a future blog on Python (as the same principles apply), but for this blog, I'll cover off the following:

  • Arrow functions
  • Array map/sort and filter methods

Most of my JS work is React based and typically involves calling API's and iterating through / manipulating the JSON responses to render UI elements like graphs, tables etc. Since this is quite formulaic and a common pattern, its nice find quicker ways to do this and to keep the code clean and tidy.

So, let's rattle off a few of these shortcuts.


Arrow functions

I think they've been around since ES5, but certainly have come to prominence in ES6. In short, it is a different way of expressing a function.

Let's consider the following function

function squared(a) { return a * a } console.log(squared(2))

Now, the same function using arrow functions:

const squared = (a) => a * a console.log(squared(2))

So we've lost a line and couple of curly brackets. A bit meh!, but when we look at map and filter, this really shines.

We can still create anonymous functions with arrow functions too:

doStuffWithCallback((a) => a * 2)

Map, Filter and Sort

Its easier to talk about these 3 methods in the same context as they array methods.

Starting with the map method, rather, not using it, and using regular for-of loop:

const albums = [ { artist: 'The Mission', album: 'Gods Own Medicine', releaseYear: 1986 }, { artist: 'The Cure', album: 'Disintegration', releaseYear: 1986 }, { artist: 'The Cult', album: 'Love', releaseYear: 1985 }, { artist: 'Marillion', album: 'Fugazi', releaseYear: 1984 }, { artist: 'Nine Inch Nails', album: 'Pretty Hate Machine', releaseYear: 1989, }, ] for (const album of albums) { console.log(album) }

And again the more traditional for loop with iterator:

const albums = [ { artist: 'The Mission', album: 'Gods Own Medicine', releaseYear: 1986 }, { artist: 'The Cure', album: 'Disintegration', releaseYear: 1986 }, { artist: 'The Cult', album: 'Love', releaseYear: 1985 }, { artist: 'Marillion', album: 'Fugazi', releaseYear: 1984 }, { artist: 'Nine Inch Nails', album: 'Pretty Hate Machine', releaseYear: 1989, }, ] for (const i = 0; i < albums.length; i++) { console.log(album[i]) }

Now with map and arrow functions:

const albums = [ { artist: 'The Mission', album: 'Gods Own Medicine', releaseYear: 1986 }, { artist: 'The Cure', album: 'Disintegration', releaseYear: 1986 }, { artist: 'The Cult', album: 'Love', releaseYear: 1985 }, { artist: 'Marillion', album: 'Fugazi', releaseYear: 1984 }, { artist: 'Nine Inch Nails', album: 'Pretty Hate Machine', releaseYear: 1989, }, ] albums.map((album) => console.log(album))

It's a better story with filter, rather than adding 'if' statements into our for loop (or possibly ternary operators), we can simply chain map and filter together. Let's say we want to sort our albums by title alphabetically:

const albums = [ { artist: 'The Mission', album: 'Gods Own Medicine', releaseYear: 1986 }, { artist: 'The Cure', album: 'Disintegration', releaseYear: 1986 }, { artist: 'The Cult', album: 'Love', releaseYear: 1985 }, { artist: 'Marillion', album: 'Fugazi', releaseYear: 1984 }, { artist: 'Nine Inch Nails', album: 'Pretty Hate Machine', releaseYear: 1989, }, ] albums.sort((a, b) => a.album > b.album).map((album) => console.log(album))

This sorts A-Z, if you want Z-A, then change greater than to less than. The sort method accepts a function with 2 variables that we use to compare current and next records. This results in a boolean return value, which the sort method uses to place that record in the array, using the merge sort algorithm.

So what about filter. Let's extend the above to filter out albums released after 1985:

const albums = [ { artist: 'The Mission', album: 'Gods Own Medicine', releaseYear: 1986 }, { artist: 'The Cure', album: 'Disintegration', releaseYear: 1986 }, { artist: 'The Cult', album: 'Love', releaseYear: 1985 }, { artist: 'Marillion', album: 'Fugazi', releaseYear: 1984 }, { artist: 'Nine Inch Nails', album: 'Pretty Hate Machine', releaseYear: 1989, }, ] albums .filter((album) => album.releaseYear > 1985) .sort((a, b) => a.album > b.album) .map((album) => console.log(album))

Typically good practice to filter first, then sort, then output.

All these methods do not mutate the underlying array, so if you need to do that assign the output to a variable (or constant, depending on what you need):

let newAlbums = albums .filter((album) => album.releaseYear > 1985) .sort((a, b) => a.album > b.album) .map((album) => console.log(album))

However, thats a bit pointless as the map contains the output to console, so more useful would be:

let newAlbums = albums .filter((album) => album.releaseYear > 1985) .sort((a, b) => a.album > b.album) newAlbums.map((album) => console.log(album))

The output of the data is done against the new array, which is already sorted and filtered. An example of this is on the Running page where the race data is sorted first from the raw data file, then each distance is chunked up with filter and map to group races by distance.


Words of Caution

Like every pattern, there are anti-patterns and more often than not, we must "think about what you are doing / trying to achieve". Some key tips:

  • Having stuff inline is great if it is used once and specific to that scope. If not, it is best to break it out as a separate function.
  • Level of complexity - these examples are quite simple sort and filter. Again, as point above, consider moving to separate function if complex. Also if you have control over the data source/API, perhaps try to get the data into the app in the correct order/format/filter - ie server side.
  • Watch out for mutations, desireable vs undesirable, chaining is great if that's the workflow you need, but consider if you need to apply parts of the chain in other functions.

Blogs