Tuesday, September 27, 2022

Advanced TypeScript/ES6 Array Features: Destructuring, Cloning, and Reducing

TypeScript Tutorials

Developing Angular applications for the past several years has opened my eyes to a whole slew of new and innovative ways of performing complex operations on JavaScript Objects with less code and increased type safety. Case in point, TypeScript’s Object destructuring and the ES6 spread operator have greatly facilitated working with arrays. In this tutorial, I’ll share how to achieve some common programming tasks on arrays while demystifying destructuring, cloning, and the reduce() function.

Destructuring Arrays in TypeScript

Destructuring is a feature of EcmaScript 2015 and Typescript that allows you to break up the structure of an entity, (i.e., an array or object) like so:

let ivoryKnightMembers = ['John', 'Rob', 'George', 'Steve'];
let [john, rob, george, steve] = ivoryKnightMembers;

The above variables will be compiled into the following JavaScript code:

var ivoryKnightMembers = ['John', 'Rob', 'George', 'Steve'];
var john   = ivoryKnightMembers[0], 
    rob    = ivoryKnightMembers[1],
    George = ivoryKnightMembers[2], 
    Steve  = ivoryKnightMembers[3];

Read: Introduction to TypeScript and Its Features

Ignoring Elements When Destructuring Array Elements

There is no rule stating that you have to account for all array elements. Here is some code that only extracts the first element:

let [first] = ivoryKnightMembers;

This next example only grabs the first two elements:

let [first, second] = ivoryKnightMembers;

We can even ignore elements as in the following example:

let [first, ,third] = ivoryKnightMembers;

Cloning an Array the ES6 Way

Copying an array is something that developers do a lot. Newbies sometimes try to copy an array by assignment using the equals (=) operator:

let arrayCopy = originalArray;

Rather than produce a copy of the array, the above code will only copy the reference to the original array. To create a real copy of an array, we need to copy over the value of the array under a new variable. Prior to ES6, we often employed the Array slice() method for this purpose. Today, we can also use the spread operator to duplicate an array:

const clonedArray = [...ivoryKnightMembers];
ivoryKnightMembers.push('Mike'); //new keyboard player
// false because clonedArray points to a new memory space
console.log(clonedArray === ivoryKnightMembers);
// ['John', 'Rob', 'George', 'Steve']
// still contains the four original members
console.log(clonedArray);

Shallow Copies Only!

Bear in mind that the spread operator only goes one level deep when copying an array. That means that top-level elements are copied, while deeper levels are referenced! Therefore, if you are trying to copy a multi-dimensional array, you will have to use a different approach.

Array Flattening in TypeScript

You can also use the ES6 spread operator to flatten multi-dimensional arrays with a depth of one:

const multiDimensionalArray = [ [1, 2], [3, 4], [5, 6] ]; 
const flattenedArray = [].concat(...multiDimensionalArray); 
console.log(flattenedArray); // [1, 2, 3, 4, 5, 6]

Another way to create a new, shallow-copied Array instance is to use the Array.from() method. It works on any array-like or iterable object and allows you to supply your own mapping function, should you require one.

Read: TypeScript Coding Outside of Angular Applications

The Array reduce() Method

Array.reduce is an extremely versatile function that lets you calculate a result from all elements of an array. It is a lot like a functional version of a foreach loop with an additional variable that you change within the loop and return at the end. I see a lot of developers sticking with loops rather than switching over because they do not quite understand how to use the reduce() method. And that is a shame, because it is really quite easy to use.

reduce() accepts two arguments:

  1. reducer: a function that accepts an element of the array and the partial result (calculated based on elements processed so far)
  2. initialResult: an optional value that will be passed to the reducer before any array elements have been processed
array.reduce(callback[, initialValue])

I recently used the reduce() method in the Tracking Selections with Checkboxes in Angular article to iterate over all of the root nodes of a MatTreeNestedDataSource, fetch their descendants, and map the names of selected nodes to a simple array:

let result = this.dataSource.data.reduce(
  (acc: string[], node: VehicleNode) => 
    acc.concat(this.treeControl
                .getDescendants(node)
                .filter(descendant => descendant.selected)
                .map(descendant => descendant.name))
  , [] as string[]);

I could have used the forEach() method to iterate over the root nodes and concat children’s names to a variable declared prior to processing the nodes:

let result: string[] = [];
this.dataSource.data.forEach((node: VehicleNode) => 
  result.concat(this.treeControl
              .getDescendants(node)
              .filter(descendant => descendant.selected)
              .map(descendant => descendant.name)));

By using reduce() instead we:

  • do not have to declare the result in advance because it will be returned by the reduce() method
  • do need to supply the accumulator (acc) parameter to the callback function so that we can access it within the function body
  • do need to provide an initial value to reduce() so that it has an array to concat to. Basically, we are moving the result variable initialization to the second method parameter as a starting point.

Conclusion to TypeScript Array Features

In this tutorial, we learned how to achieve some common programming tasks on arrays using some modern ES6 and TypeScript techniques, namely destructuring, cloning, and the reduce() function. Although you could probably get away with eschewing cutting-edge techniques like these for your entire career, it’s good to have options when tackling a problem.

Read: Explore TypeScript Fat Arrow

Robert Gravelle
Robert Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Popular Articles

Featured