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;
var ivoryKnightMembers = ['John', 'Rob', 'George', 'Steve']; var john = ivoryKnightMembers, rob = ivoryKnightMembers, George = ivoryKnightMembers, Steve = ivoryKnightMembers;
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.
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:
- reducer: a function that accepts an element of the array and the partial result (calculated based on elements processed so far)
- initialResult: an optional value that will be passed to the reducer before any array elements have been processed
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.