If you’re working with arrays in TypeScript, you’ve probably come across the map() function. The map() function is a powerful tool that allows you to transform each element of an array into a new element, based on a function that you define.

Here’s what the function looks like:

const array: unknown[];

array.map((value, index, array) => {
    
})

The map function accepts a function that takes a single item in the array, the index of the item, and the whole array as parameters. The function doesn’t have to use all of these, most commonly you might just need the value.

Let’s look at an example of how to use the map function in TypeScript:

const numbers = [1, 2, 3, 4, 5];

const doubledNumbers = numbers.map((num) => {
  return num * 2;
});

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

In this example, we have an array of numbers. We then call the map function on this array and provide a callback function that doubles each number. The doubledNumbers array is the result of the map function, and contains the transformed values.

Of course, we could do this in plenty of other different ways, but one of the key benefits of the map function is that it allows you to write more concise and readable code. Instead of using a for loop or other iteration methods, you can use the map function to transform each element of an array in a single line of code. You can even extract the logic inside the callback function into a separate function, letting you write cleaner, more reusable code.

Another benefit of the map function is that it creates a new array, much like the spread operator, leaving the original array intact. This is really useful when you want to make changes to an array without affecting the original data. This immutability is especially useful in frameworks like React, which work a lot with immutable data.

Advanced Usage

You might notice here that we haven’t used the other parameters available to our callback function, so let’s explore a use case for each.

Let’s imagine we have a list of names:

const names = ['Aaron', 'Brian', 'Carl', 'Ruby', 'Omari'];

That we want to transform into an array of this type:

type User = {
    id: number;
    name: string;
};

By filling in this function:

function generateUsers(names: string[]): User[] { 
// ??
}

Obviously we know where the name field will come from, but what about the id? We know a for loop gives us an index value, so let’s try it with that:

function generateUsers(names: string[]): User[] {
    const users: User[] = [];
    for (let i = 0; i < names.length; i++) {
        users.push({name: names[i], id: i})
    }
    return users;
}

And this function will work, there’s nothing strictly wrong with this approach, but we can be a little smarter with the map function. Here’s an approach using the map function:

function generateUsers(names: string[]): User[] {
    return names.map((name, i) => ({ name, id: i }));
}

This approach is a lot shorter, and you no longer have to think about indexing the names array, or creating a new variable for the users array.

Now let’s take a look at an example that uses the array parameter. We’ll change our array a bit, so now we have some duplicate names:

const names = ['Aaron', 'Brian', 'Carl', 'Ruby', 'Omari', 'Brian', 'Aaron'];

How you might want to handle these might depend on your situation, but I just want to assume they’re the same person. This means I want to give them the same id, but how do I do that if I’m using the index as the id?

function generateUsers(names: string[]): User[] {
    return names.map((name, i, array) => ({ name, id: array.indexOf(name) }));
}

Instead of using the index, we can search the original array for the first time the name appears. Then we could go ahead and merge these users together, using the identical ids.

Nested Maps

You might also run into a scenario where you’re trying to map over a 2D array. Map can easily be used for this. you simply need to call map for each dimension of your array. e.g. for a 2D array:

const grid = [
    [0, 1, 0, 1, 0],
    [1, 0, 0, 1, 0],
    [0, 1, 0, 1, 0],
    [1, 0, 0, 1, 0],
    [0, 1, 0, 1, 0],
];

const flippedGrid = grid.map((row) => row.map((value) => 1 - value));

Here we’re starting off with a 2D array. Mapping over this 2D array, our callback function receives each row as a parameter. Then we can map over each item in each row, by mapping over each row.

Mapping Objects

Map is an array function, so you might be wondering how to use it to iterate over objects, or what that even means for an object. Luckily, TypeScript offers some methods to easily transform your object into an array.

For example, let’s image we’re making a banking app, that stores the balances of some users:

const users = {
    adam: { balance: 1000 },
    brian: { balance: 25000 },
    carl: { balance: 10 },
};

And we’ll write some code that can subtract some money from every balance.

We can transform this object into an array using Object.entries:

const entries = Object.entries(users);
/*
[
  [ 'adam', { balance: 1000 } ],
  [ 'brian', { balance: 25000 } ],
  [ 'carl', { balance: 10 } ]
]
*/

Then map over this array:

const updatedUsersArray = entries.map(([key, value]) => [key, { balance: value.balance - 200 }]);

And put our object back together using Object.fromEntries:

const updatedUsers = Object.fromEntries(updatedUsersArray);

We can put this all together into a handy function, using some generics:

function mapObject<T, U>(
    obj: Record<string, T>,
    fn: (
        entry: [string, T],
        index: number,
        entries: [string, T][]
    ) => [string, U]
) {
    const entries = Object.entries(obj);
    const newEntries = entries.map(fn);
    return Object.fromEntries(newEntries);
}

And use it like:

const updatedUsers = mapObject(users, ([key, value]) => [
    key,
    { balance: value.balance },
]);

Conclusion

Thanks for reading!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.