ES6 array patterns for immutability

October 16, 2016
Orange Tiles in Visakhapatnam, India
Share post

Here is a collection of snippets for manipulating arrays using es6 features in an immutable way aiming towards a more functional style of programming. In a subsequent post, I'm going to write about similar patterns but with objects.

Further, these patterns help if for whatever reason you cannot use immutable.js in your project.

Getting first element

What if you want to get the first element of the array? What do you do? I used to do:

  const first = anArray[0];

Using array destructuring, in es6, you can now do instead:

  const [first] = anArray;

Immutable shift

The Array::shift method can be used to remove an element from the beginning of the array. If you want to keep the original array unchanged while performing an operation equivalent to shift you can do:

const fruits = ['apple', 'orange', 'pear'];
const [apple, ...restOfFruits] = fruits;
//restOfFruits contains ['orange', 'pear']

Immutable push

Add to the end of the array. Just like Array::push

const fruits = ['apple', 'pear']
const moreFruits = [...fruits, 'orange']
//moreFruits now contains ['apple', 'pear', 'orange']

Immutable push/unshift

You can generalize the pattern this way.

const fruits = ['apple', 'pear'];
const moreFruits = ['grape', ...fruits, 'orange'];
//moreFruits now contains ['grape','apple', 'pear', 'orange']

Added benefit: they are expressions

In other words, they always return/represent a value. This means you can cleanly use them for writing a more functional style javascript. For instance in a reduce operation. Like so:

const fruits = ['apple', 'pear'];
const reverseFruits = fruits.reduce((acc, fruit) => [fruit, ...acc], []);
// reverseFruits contains ['pear', 'apple']

You can read more about expressions and statements here.

Full example

const expect = require('expect.js')

const fruits = ['apple', 'melon', 'lemon']
const originalFruits = fruits;

// get, immutable shift
const [apple, ...restOfFruits] = fruits

// yep, we got the first element correctly
expect(apple).to.be(fruits[0])

//restOfFruits doesn't have first element anymore
//we pseudo-shifted
expect(restOfFruits).to.eql(['melon','lemon'])

// and fruits reference still pointing to the original array
// and also we didn't mutate it
expect(originalFruits).to.be(fruits)
expect(fruits).to.eql(['apple', 'melon', 'lemon'])

// immutable push
const moreFruits = [...fruits, 'orange']
expect(moreFruits).to.eql(['apple', 'melon', 'lemon', 'orange'])

// immutable insert
const eventMoreFruits = ['pizza', ...fruits, 'grape']
expect(eventMoreFruits).to.eql(['pizza', 'apple', 'melon','lemon','grape'])

// use as expression
const reverseFruits = fruits.reduce((acc, fruit) => [fruit, ...acc], [])
expect(reverseFruits).to.eql(['lemon', 'melon', 'apple'])

const result = {
  fruits,
  restOfFruits,
  moreFruits,
  eventMoreFruits,
  reverseFruits
}