{"id":11355,"date":"2022-01-12T02:21:05","date_gmt":"2022-01-12T02:21:05","guid":{"rendered":"https:\/\/www.htmlgoodies.com\/?p=11355"},"modified":"2022-01-18T03:35:49","modified_gmt":"2022-01-18T03:35:49","slug":"typescript-array-features","status":"publish","type":"post","link":"https:\/\/www.htmlgoodies.com\/javascript\/typescript-array-features\/","title":{"rendered":"Advanced TypeScript\/ES6 Array Features: Destructuring, Cloning, and Reducing"},"content":{"rendered":"

\"TypeScript<\/p>\n

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()<\/strong> function.<\/p>\n

Destructuring Arrays in TypeScript<\/h2>\n

Destructuring<\/em> 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:<\/p>\n

let ivoryKnightMembers = ['John', 'Rob', 'George', 'Steve'];\r\nlet [john, rob, george, steve] = ivoryKnightMembers;\r\n<\/pre>\n

The above variables will be compiled into the following JavaScript code:<\/p>\n

var ivoryKnightMembers = ['John', 'Rob', 'George', 'Steve'];\r\nvar john   = ivoryKnightMembers[0], \r\n    rob    = ivoryKnightMembers[1],\r\n    George = ivoryKnightMembers[2], \r\n    Steve  = ivoryKnightMembers[3];\r\n<\/pre>\n

Read:<\/strong> Introduction to TypeScript and Its Features<\/a><\/p>\n

Ignoring Elements When Destructuring Array Elements<\/h3>\n

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

let [first] = ivoryKnightMembers;\r\n<\/pre>\n

This next example only grabs the first two elements:<\/p>\n

let [first, second] = ivoryKnightMembers;\r\n<\/pre>\n

We can even ignore elements as in the following example:<\/p>\n

let [first, ,third] = ivoryKnightMembers;\r\n<\/pre>\n

Cloning an Array the ES6 Way<\/h2>\n

Copying an array is something that developers do a lot. Newbies sometimes try to copy an array by assignment using the equals<\/em> (=<\/strong>) operator:<\/p>\n

let arrayCopy = originalArray;\r\n<\/pre>\n

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()<\/strong> method for this purpose. Today, we can also use the spread<\/strong> operator to duplicate an array:<\/p>\n

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

Shallow Copies Only!<\/h3>\n

Bear in mind that the spread<\/strong> 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.<\/p>\n

Array Flattening in TypeScript<\/h3>\n

You can also use the ES6 spread operator to flatten multi-dimensional arrays with a depth of one:<\/p>\n

const multiDimensionalArray = [ [1, 2], [3, 4], [5, 6] ]; \r\nconst flattenedArray = [].concat(...multiDimensionalArray); \r\nconsole.log(flattenedArray); \/\/ [1, 2, 3, 4, 5, 6]\r\n<\/pre>\n

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

Read:<\/strong> TypeScript Coding Outside of Angular Applications<\/a><\/p>\n

The Array reduce() Method<\/h2>\n

Array.reduce<\/strong> 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<\/strong> 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()<\/strong> method. And that is a shame, because it is really quite easy to use.<\/p>\n

reduce()<\/strong> accepts two arguments:<\/p>\n

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

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

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

    I could have used the forEach()<\/strong> method to iterate over the root nodes and concat<\/strong> children’s names to a variable declared prior to processing the nodes:<\/p>\n

    let result: string[] = [];\r\nthis.dataSource.data.forEach((node: VehicleNode) => \r\n  result.concat(this.treeControl\r\n              .getDescendants(node)\r\n              .filter(descendant => descendant.selected)\r\n              .map(descendant => descendant.name<\/a>)));\r\n<\/pre>\n

    By using reduce()<\/strong> instead we:<\/p>\n