Array Mapping Methods

June 5, 2024

Typically in React when you want to render items based on data in an array, you use map as in something like this:

return (
  <ul>
    {items.map((item) => (
      <li key={item.id}>{item.title}</li>
    ))}
  </ul>
);

This works fine most of the time — but sometimes you don't have an array to start with. I was recently reading a post by Josh Comeau in which he provides a few solutions. I've run into this problem from time-to-time, and wanted to explore the options.

Say you want to display a gold star icon based on rating. The rating prop will be a number from 0 to 5 and you want to show 0 to 5 stars. One way to do this is to make an array and render the array.

const Stars = ({ rating }) => {
  const stars = [];
  for (let i = 0; i < rating; i++) {
    stars.push(<StarIcon />);
  }

  return <div className="star-wrapper">{stars}</div>;
};

He prefers to create a range utility, then use it with map:

function range(start, end, step = 1) {
  if (end === "undefined") {
    end = start;
    start = 0;
  }
  const arr = [];
  for (let i = start; i < end; i += step) {
    arr.push(i);
  }

  return arr;
}

// in a Stars comnponent where `rating` is a number from 0 to 5
return (
  <div className="star-wrapper">
    {range(rating).map((num) => (
      <StarIcon key={num} />
    ))}
  </div>
);

I quite like this solution that leverages a lesser-known aspect of Array.from, which is that it takes two arguments: an iterable (e.g. an array or array-like object) and a function that maps over the iterable.

const arrayOfRandomNumbers = Array.from({ length: 10 }, () =>
  Math.floor(Math.random() * 10)
);
// returns an array like this: [2, 3, 9, 3, 1, 7, 0, 4, 3, 5]

This approach can work with the range method, too:

const range = (start, end, step = 1) => Array.from(
  { length: (end - start) / step },
  (_, i) => start + i * step;
)