Here’s how you can make better use of JavaScript Arrays

This article is short and concise, I promise. Over the past few months, I’ve noticed that four errors (regarding array usage) frequently occur in the pull requests I’ve reviewed. At the same time, I make these mistakes myself, hence this article. Let’s learn to make sure we use array methods correctly in the future!

useArray.includesalternativeArray.indexOf

“If you need to find an element in an Array, use array.indexof.”

I remember one of my JavaScript classes saying something like this. Without a doubt, this is absolutely true!

In the MDN document, array. indexOf is described as: return the first index in the Array where a given element can be found, or -1 if it does not exist. Therefore, if you need an index (for a given element) later in the code, then array.Indexof is the best choice.

However, what if all we need to know is whether the array contains a given element? That means it’s just a yes or no, and it’s a Boolean question. In this case, I recommend using Array.includes, which returns a Boolean value directly.

'use strict';

const characters = [
  'ironman',
  'black_widow',
  'hulk',
  'captain_america',
  'hulk',
  'thor',
];

console.log(characters.indexOf('hulk'));
// 2
console.log(characters.indexOf('batman'));
// -1

console.log(characters.includes('hulk'));
// true
console.log(characters.includes('batman'));
// false
Copy the code

useArray.findalternativeArray.filter

Array.filter is a very useful method. It filters the original array through a callback function and returns the filtered items as a new array. As the name suggests, we use this method for filtering and (generally speaking) get a new array of shorter length.

However, I don’t recommend using Array.filter if you know that only one item will be left after the callback filter. For example, to filter an array with an ID equal to a unique ID. In this example, array.filter returns a new Array with only one item. However, this new array is useless just to get the item with a specific ID.

Let’s talk about performance. To get all the items that match the callback’s filter criteria, array. filter must traverse the entire Array. If there are thousands of items in the original array, the number of times the callback function needs to be executed is considerable.

To avoid these situations, I recommend using Array.find. It requires a callback function like array.filter, but only returns the first item that meets the criteria. When it finds the first element that matches the callback’s filter criteria, it immediately stops searching. No longer traversing the entire array.

'use strict';

const characters = [
  { id: 1, name: 'ironman' },
  { id: 2, name: 'black_widow' },
  { id: 3, name: 'captain_america' },
  { id: 4, name: 'captain_america' },
];

function getCharacter(name) {
  return character => character.name === name;
}

console.log(characters.filter(getCharacter('captain_america')));
// [
//   { id: 3, name: 'captain_america' },
//   { id: 4, name: 'captain_america' },
// ]

console.log(characters.find(getCharacter('captain_america')));
// { id: 3, name: 'captain_america' }
Copy the code

useArray.somealternativeArray.find

I admit I often make this mistake. Later, a friend suggested that I look at the MDN documentation to find a better way. In fact, this error) and the above Array. The indexOf/Array. Includes examples are very much alike.

In the example above, we know that array. find takes a callback function as an argument and returns the first element. However, when we need to know whether an element exists in an Array, is array. find the best choice? Not necessarily, because it returns an element, not a Boolean.

In the example below, I recommend using array. some, which returns the Boolean value you need.

'use strict';

const characters = [
  { id: 1, name: 'ironman', env: 'marvel' },
  { id: 2, name: 'black_widow', env: 'marvel' },
  { id: 3, name: 'wonder_woman', env: 'dc_comics' },
];

function hasCharacterFrom(env) {
  return character => character.env === env;
}

console.log(characters.find(hasCharacterFrom('marvel')));
// { id: 1, name: 'ironman', env: 'marvel' }

console.log(characters.some(hasCharacterFrom('marvel')));
// true
Copy the code

There is a difference between Array. Some and Array. Includes. Both return a Boolean value indicating whether an item is present in the array and stop traversing the array once it is found. The difference is that the argument to array. some is a callback function, while the argument to array. includes is a value (regardless of the second optional argument).

Suppose you want to know whether an item with a value exists in an Array. You can either write code: [].includes(value), or you can pass item = > to array.some. Item === value as a callback function. Array.includes is easier to use and array. some is more manipulable.

useArray.reducealternativeArray.filterArray.mapThe combination of

In fact, Array.Reduce is not easy to understand. However, if we first use array.filter to filter the original Array, then call array.map (for the result) (to get a new Array). That seems like a bit of a problem. Is there something we’re missing?

The problem with this is that we iterate through the array twice. The first time is to filter the original Array to get a new Array of shorter length, and the second time is to process the new Array returned by array.filter, creating a new Array again! To get the final result, we used a combination of two array methods. Each method has its own callback function, and the temporary Array used by Array.map is provided by Array.filter, which (in general) is not reusable.

To avoid such an inefficient scenario, my recommendation is to use Array.Reduce. Same result, better code! Array.reduce allows you to put filtered, cut items into the accumulator. An accumulator can be a number to be incremented, an object to be populated, a string or array to be concatenated, and so on.

In the example above, we used array. map, but it is (more) recommended to use the accumulator for array.Reduce for the Array to be concatenated. In the following example, depending on the value of the variable env, we either add it to the accumulator or leave the accumulator unchanged (that is, do nothing).

'use strict';

const characters = [
  { name: 'ironman', env: 'marvel' },
  { name: 'black_widow', env: 'marvel' },
  { name: 'wonder_woman', env: 'dc_comics' },
];

console.log(
  characters
    .filter(character => character.env === 'marvel')
    .map(character => Object.assign({}, character, { alsoSeenIn: ['Avengers'] }))
);
// [
//   { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] },
//   { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] }
// ]

console.log(
  characters
    .reduce((acc, character) => {
      return character.env === 'marvel'
        ? acc.concat(Object.assign({}, character, { alsoSeenIn: ['Avengers'] }))
        : acc;
    }, [])
)
// [
//   { name: 'ironman', env: 'marvel', alsoSeenIn: ['Avengers'] },
//   { name: 'black_widow', env: 'marvel', alsoSeenIn: ['Avengers'] }
// ]
Copy the code

That’s all for this article!

I hope that’s helpful. If you have any comments on this article or examples that need to be discussed, please let me know in the comments. If you like this post, please give me a thumbs up 👏. And share it with more friends. Thanks for reading!

PS: You can follow me on Twitter.

Note: Please check whether your browser supports these methods before using array. find and array. includes. These methods are not supported by Internet Explorer.