Functional Style TypeScript 1: Array.map

Lesson 1: Array.map

Overview

Array.map provides a declarative API for iterating over an array elements with the intention of building a new array of the same size. Array.map returns a new array instead of mutating the original array in place.

Scenario

Use Array.map when you want to build a new array of the same size by applying some processing logic to each element one at a time.

Example

Let's say that we are building some test data. We want to have an array of users with the correct property types, but we don't really care about the content.

type User{
    id: String;
    name: String;
    userSinceYear: number;
}

currentYear: number = 2023;

testUsers: User[] = ['123','124','125'].map(
	(id,index) =>({
    	id,
        name: `System  User (${id})`,
        userSinceYear: currentYear - index
    })
);
Example 1

Note that in our arrow function, we use ({...}) to indicate the implicit return of an object. We don't need the return keyword to inline a value, but since {...} indicates the body of an arrow function, we need to escape the default use of {...} by wrapping our object literal in (...).

Example 1 will have the same effect as the following, more brute-force code.

testUsers: User[] = [
	{
    	id: '123',
        name: "System User (123)",
        userSinceYear: 2023
    },
    	{
    	id: '124',
        name: "System User (124)",
        userSinceYear: 2022
    },
    	{
    	id: '123',
        name: "System User (125)",
        userSinceYear: 2021
    },
]
Example 2

In other words, we have taken an array of string IDs and built an object from each using the logic specified in the callback function we handed to Array.map.

API

The callback you feed into Array.map specifies the logic that will be applied to each element in the array in building a new array as the first parameter. You can access the index of the given element via the second parameter of your callback function. This is pretty convenient, especially if you've ever tried to do these things with Java's Stream API!

Note that if using typescript, you don't need to provide a type for the parameters of your arrow function. The first parameter will be of the same type as a single element of your array and the second parameter will be a number. Finally, you don't have to introduce an index parameter unless you need it. The following is perfectly fine.

testDocuments: TestDocument[] = testIds.map((id) => new TestDocument(id));

Immutability

Array.map(callback) returns a new Array and does not mutate the original. This means that you typically need to store a reference to the result to have any effect. Do be aware that if your array itself holds objects you may need to take care to clone these or nested properties to avoid deeply shared references with the original array.

Use Cases

You typically want the callback you provide Array.map to be a pure function with no side effects. If you want to perform one side effect per element in an array, use Array.forEach instead.

Point Free

We can make the above example "point free" by naming the arrow function we provide to Array.map.

type User{
    id: String;
    name: String;
    userSinceYear: number;
}

currentYear: number = 2023;

const buildTestUserFromId = (id: string, index: number): User =>({
    	id,
        name: `System  User (${id})`,
        userSinceYear: currentYear - index
    });

testUsers: User[] = ['123','124','125'].map(buildTestUserFromId);
);
Example 3

Exercises

  1. Given a string[] rawWords, create a new String [] called tokens that consists of the original words in lower case and with the symbols ".", "?", and "!" removed.
  2. Create a number[] called integers that holds the numbers from 1 to 100. Create a second number [] called perfectSquares that has the squares of the numbers in integers.
  3. Given a string[] called words, calculate an array of tuples of type [string,number] where the first member of each tuple is the string from the original array, words, and the second member is an integer representing the number of vowels in the given word.

Given: ["hat","ball","todo"]

Return: [["hat",1],["ball",1],["todo",2]]