Arrays are everywhere in JavaScript, but it’s easy to make a mess of them and make it almost impossible to test. Here’s a quick summary of useful functions you can use on every array — without mutating the original. Arrays are used in almost every language, so even though the functions listed are in JavaScript arrays, the concepts are the same among other languages.

Map

The Array.map function is one of the most common functions you can use on an array. It takes a function as a parameter and will call the function on each iteration while looping the array it is called on.


  // Array.prototype.map
  const numbers = [1, 2, 3, 4, 5];
  const doubled = numbers.map(n => n * 2);
  console.log(doubled); // [2, 4, 6, 8, 10]
  console.log(numbers); // [1, 2, 3, 4, 5]

Filter

The Array.filter function provides a way to remove items from an array that don’t pass a test in a function that is provided to the filter function. It loops through each item in the array and returns the item if the function evaluates (returns) true, otherwise it is excluded from the filtered array.


  // Array.prototype.filter
  const numbers = [1, 2, 3, 4, 5];
  const filtered = numbers.filter(n => n > 2);
  console.log(filtered); // [3, 4, 5];
  console.log(numbers); // [1, 2, 3, 4, 5];

Concat

The Array.concat function (similar to String.concat) allows you to add new items to an array without modifying the original. Typically, where you can use Array.push you could almost certainly use Array.concat instead. You can pass either a single item to be added to the array, or another array entirely, which will then be joined to the existing array.


  // Array.prototype.concat
  const numbers = [1, 2];
  const concatenatedNumber = numbers.concat(3);
  const concatenated = concatenatedNumber.concat([4, 5]);
  console.log(concatenated); // [1, 2, 3, 4, 5];

Slice

The Array.slice function gives you a way to remove items from an array, without modifying the original. Commonly confused with splice, but splice will mutate the original array.


  // Array.prototype.slice
  const numbers = [1, 2, 3, 4, 5];
  const selectedNumbers = numbers.slice(0, 2); 
  console.log(selectedNumbers); // [1, 2]
  console.log(numbers); // [1, 2, 3, 4, 5]

Reduce (or ReduceRight)

The Array.reduce and Array.reduceRight functions aim to take an array and convert it to a single value. The reducer function will iterate over the items in the array from left to right, whereas Array.reduceRight will start at the right and work its way to the left. In each iteration, the predicate function will pass in the previous value (the result of running the last iteration, or the initial value), and the current item in the array. It can seem a bit more complicated and confusing but understanding reduce is key to understanding libraries like Redux which uses the idea of reducers in a very cool way!


  // Array.prototype.reduce || Array.prototype.reduceRight
  const numbers = [1, 2, 3, 4, 5];
  const sum = numbers.reduce((prev, curr) => prev + curr, 0);
  console.log(sum); 15

In the example above, the initial value is 0 and in each iteration, it will take 0 or the previous result and add it to the current item. The result of this reduce function will be a single value — 15. You can think of reducers as much more powerful than I’ve led on here, and they can be.

Why are we trying not to mutate any arrays? Can’t I just call .push or .splice?

Using the functions I’ve outlined above and assigning the result to a new variable, rather than mutating the original array means you can see a much more explicit flow in your code. It makes it much easier to use these functions in other functions, especially when you’re writing tests and are expecting predictable results. A common rule of thumb in a lot of programming languages, but perhaps is most prevalent in JavaScript is:

Just because you can, doesn’t mean you should..

It’s possible to mutate an array with .push for example, but what if you wanted a reference to that array later in your code, after the push happens? You will need to either create another reference to the original array before it is mutated or refactor the .push to use a non-mutating method then write your new code — or you can just use a pure function from the start!