All Articles

Map Isn't Just for Arrays

Arrays are Mappable

Most of us are familiar with JavaScript’s map function: it turns an array into a new array by transforming each item according to some function.

Turn everything into 1s

To create an arrays of 1s from an old array, just return 1 in your mapping function.

numbers = [1, 2, 3, 4, 5];
doubledNumbers = numbers.map(() => 1);
// [1, 1, 1, 1, 1]

Not very useful, though. Since our mapping function has access to the current value being processed, why not use it?

Double everything

numbers = [1, 2, 3, 4, 5];
doubledNumbers = numbers.map((x) => x * 2));
// [2, 4, 6, 8, 10]

Get everyone’s full name

users = [
  {
    firstName: 'Bruce',
    lastName: 'Banner'
  },
  {
    firstName: 'Tony',
    lastName: 'Stark'
  },
  {
    firstName: 'Peter',
    lastName: 'Parker'
  }
];
userFullNames = users.map((u) => `${u.firstName} ${u.lastName}`);
/* [
"Bruce Banner",
"Tony Stark",
"Peter Parker"
]*/

Turn everyone’s name into JSX (React)

users = [
  {
    firstName: 'Bruce',
    lastName: 'Banner'
  },
  {
    firstName: 'Tony',
    lastName: 'Stark'
  },
  {
    firstName: 'Peter',
    lastName: 'Parker'
  }
];
usersList = users.map((u) => (
  <li>
    {u.firstName} {u.lastName}
  </li>
));
/* [
"Bruce Banner",
"Tony Stark",
"Peter Parker"
]*/

Since most applications deal with lists of things (friends list, contacts, messages, social media feeds, etc), it’s great that JavaScript provides first-class mapping support for arrays.

The idea behind map, however, goes much further.

All Boxes are Mappable

Think of array as simply a box, and map lets you transform the inner values without affecting the box itself. If you call map on an array of 10 items, you’ll always get back a new array of 10 items.

Objects are Mappable

An object is basically an array, but the indices don’t have to be 0, 1, 2, 3, 4, etc. It can be used as a box too!

Here’s a simple mapObject function.

mapObject = (transformFunction, object) => {
  const newObject = {};
  for (let key in object) {
    const value = object[key];
    const transformedValue = transformFunction(value);
    newObject[key] = transformedValue;
  }
  return newObject;
};

I’ve found this useful when validating form fields. Let’s say the four fields, name, age, job, and email are all required.

formFields = {
  name: 'Yazeed Bzadough',
  age: undefined,
  job: 'Front-End Developer',
  email: ''
};

mapObject((field) => (field ? 'Looks good' : 'Required!'), formFields);

/* {
"name": "Looks good",
"age": "Required!",
"job": "Looks good",
"email": "Required!"
}*/

Strings are Mappable

Strings are even easier to map, because their indices are zero-based! We can create a mapString function using ES6 spread operator.

Using the index, we can alt case a string.

mapString = (transformFunction, string) =>
  [...string].map(transformFunction).join('');

altCase = (char, index) => (index % 2 === 0 ? char.toUpperCase() : char);

mapString(altCase, 'Get in the car!');
// "GeT In tHe cAr!"

Promises are Mappable

We can’t loop over a Promise or turn it into an array, but that’s not even necessary!

Using its native .then method, we can create a new Promise with the transformed inner value. It’s still mapping!

Here’s a simple example

Promise.resolve(' Hello ')
  .then((s) => s.trim())
  .then((s) => s.concat(', World!'))
  .then((s) => s.toUpperCase())
  .then(console.log);
// HELLO, WORLD!

How about we fetch the first 30 GitHub users?

fetch('https://api.github.com/users')
  .then((response) => response.json())
  .then((users) => {
    console.log(users);
  });
// [{..}, {..}, {..}]

In that example, we’re mapping an HTTP response into a JSON object. You can go even further and get all the logins from the list.

fetch('https://api.github.com/users')
  .then((response) => response.json())
  .then((users) => users.map((u) => u.login))
  .then(console.log);
// ['...', '...', '...']

Functors

All of these data structures can be considered functors or functor-like. This is a data type used in functional programming for transforming all sorts of boxed inner values.

If you’d like a deeper explanation, I briefly introduce functors in my free RamdaJS course, and also refer you to this great article on the subject.

Thanks for reading!