Seven advanced techniques for Javascript spread syntax and rest parameters

Spread syntax and rest parameters are two sides of the same coin. While the first gives you access to all properties of an object or array, the latter 'compresses' several variables or arguments into a single array. Read on to learn how to make the best use of this syntactic gemstone collection

The three dots ... that represent both of these concepts were introduced with ECMAScript2015. They literally "spread" an array or an object into its elements, which provides you with a great variety of options while coding. For example:

  • Destructuring single or multiple elements.
  • Concat multiple elements into one.
  • Cherry-pick variables you need and sort out those you don't.

I have collected seven common use cases from my latest projects that make use of this flexible approach. Each is as simple as it is powerful.

1) Get rid of unwanted object properties

The idea is simple:

  • Assume you have a Javascript object out of which you need all props but one.
  • You also need the original object at a later point.

You could of course use the delete operator and keep a copy of the original object. Using spread syntax, however, keeps your code more readable.

const domeOfCologne = {
  buildStart: 1248,
  buildHalted: 1560,
  buildFinished: 1880,
  buildingCity: 'Cologne',
  buildingCountry: 'Germany'
}

// Remove the location
const { buildingCountry, ...domeInfo } = domeOfCologne; 

console.log(buildingCountry) // output: 'Germany'
console.log(domeInfo)        // output: {buildStart: 1248, /* ... */}

2) Join elements of several arrays together

A quite common use case is to join elements from distinct arrays. It is as easy as spreading the old arrays and adding them into a new one. Further below, you can also find a more complex method that considers nested arrays as well.

const americanNames = ['Dave', 'Joe', 'John']; 
const germanNames = ['Hans', 'Mertens', 'Astrid'];
const englishNames = ['James', 'Peter', 'Boris']; 

const allNames = [...americanNames, ...germanNames, ...englishNames];
console.log(allNames) // Logs an array including all elements from above

3) Add optional function arguments

Rest parameters in a function's declaration make it easy to define optional arguments. The following function will take in one mandatory argument. It will then apply a function based on its value, using the other arguments passed into the function.

function math(calcMeth, ...numbers) {
  numbers.forEach(num => {
    if (isNaN(num)) {
      throw new TypeError('One or more arguments are not numeric')
    }
  });
  switch (calcMeth) {
    case 'sum':
      return numbers.reduce((prev, curr) => prev + curr, 0);
    case 'multi':
      return numbers.reduce((prev, curr) => prev * curr, 1);
    case 'power': 
        return numbers.reduce((prev, curr) => prev ** curr);
    default: 
      throw new Error(`${calcMeth} is not a valid calculation method`)
  }
}

console.log(math('sum', 2, 3))   // 5
console.log(math('multi', 2, 3)) // 6
console.log(math('power', 2, 3)) // 8

4) Extend an object with additional properties

This technique comes in especially handy when using standard configuration and dynamically extend it. A case in which I've used it myself was with a REST API endpoint that always delivers a standard HTTP - message. I would use the spread operator to additionally add a custom response from the database the frontend dev could then process  

const standardResponse = {
  status: 400,
  title: 'client-error', 
  message: 'This item is already maintained in the database'
}

const extendedResponse = {
  ...standardResponse, 
  itemId: '123'
}

5) Copy an array or an object

If you want to keep the original state of an item, copying it is a save option. Like this, you have it available for further processing. This might also come in especially handy in reactive contexts, as the copy will be non-reactive.

Note that the new object also has no more reference to the original
const original = {
  name: 'Mona Lisa', 
  creator: 'Leonardo Da Vinci'
}

const copy = { ...original }; 

console.log(copy) // {name: 'Mona Lisa', creator: 'Leonardo Da Vinci'}
console.log(copy === original) // false

6) Remove duplicated objects from an array

This is a more complex one, but it makes perfect use of the spread operator.

  • array.map will pull out the values
  • new Map will make sure that only unique objects are kept
  • ... will iterate over the map and create the array
const pictures = [
  {
    name: 'Mona Lisa',
    painter: 'Leonardo Da Vinci',
  },
  {
    name: 'The Scream',
    painter: 'Edvard Munch',
  },
  {
    name: 'Starry Night',
    painter: 'Vincent van Gogh'
  },
  {
    name: 'The Scream',
    painter: 'Edvard Munch',
  }, {
    name: 'Mona Lisa',
    painter: 'Leonardo Da Vinci',
  },
];

function removeDuplicates(arr) {
  return [...new Map(arr.map((item) => [item.name, item])).values()];
}

console.log(removeDuplicates(pictures)); // Logs the three unique paintings

7) Flatten nested arrays

Nested arrays are nasty. And without libraries such as lodash, you often end up with unnecessarily spaghettified code. What if there was an easier way?

The following function will recursively iterate through an array. It pulls out all elements nested within and joins them in a new array.

const churchesOfMadrid = [
  [
    'Templo de Debod',
    'Iglesia Parroquial de San Jerónimo el Real',
    [
      'San Antonio de los Alemanes',
      'Convento de las Trinitarias Descalzas de San Ildefonso',
      [
        'Basílica de San Francisco el Grande',
        'Basílica Pontificia de San Miguel'
      ],
    ],
  ]
]

function flattenArray(array) {
  const newArray = [];
  const length = array.length
  for (let index = 0; index < length; index++) {
    if (Array.isArray(array[index])) {
      newArray.push(...flattenArray(array[index]))
    } else {
      newArray.push(array[index])
    }
  }
  return newArray
}

const newArray = flattenArray(churchesOfMadrid);
console.log(newArray) // Returns the flattened array item

References

Spread - and Rest Syntax on MDN

Spread syntax (...) - JavaScript | MDN
Spread syntax (...) allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object…
Rest parameters - JavaScript | MDN
The rest parameter syntax allows a function to accept an indefinite number of arguments as an array, providing a way to represent variadic functions in JavaScript.