JavaScript: Stop using .indexOf() and .filter()

Photo by <a href="https://unsplash.com/@gamell" target="_blank">Joan Gamell</a> on <a href="https://unsplash.com" target="_blank">Unsplash</a>

Photo by Joan Gamell on Unsplash

I'm a JavaScript developer. I sort of love JavaScript. But what I don't love is how some of my worst practices have continued to pervade my coding standard for years. And I'm finally going to do something about it. I'm going to write down one of my biggest pet peeves and explain what is better. You probably have gathered by the title, what I'm going to go over. So sit tight and enjoy the ride. Or sit loose and grumble the entire time. This post is for me, not you.

First thing's first, though.

There are good reasons to use the String.prototype.indexOf() and Array.prototype.filter() methods. For example, you're trying to find where a certain substring begins in a string; or you want to only display items in a collection of people where the last name is "Smith." That's where they're fine. But where they are not fine are in instances like the following:


  const header = `I don't know where the bathroom is.`;

  if (header.indexOf(`loo`) === -1) {
    console.warn("This header has no British slang.");
  }

While the above code will work just fine, it's not really doing what you want. The better string method to use here is .includes()


const header = `I don't know where the bathroom is.`;

if (!header.includes(`loo`)) {
  console.warn("This header has no British slang.");
}

That code snippet is much more clear in its intent, and uses fewer characters to write! Now, this is pretty much the only wrong use of .indexOf(). String.prototype .indexOf() is a String method, but it is also an Array method, and the Array method can be used just as incorrectly! At least Array has an .includes() method that works the same way! Consider the following:


const myFavoriteFruits = [
  'apple',
  'grape',
  'kiwi',
  'pear',
  'pineapple',
  'strawberry'
];

// WRONG
if (myFavoriteFruits.indexOf('apple') !== -1) {
  console.warn("I don't like apples,");
}

// RIGHT
if (myFavoriteFruits.includes('apple')) {
  console.warn("I don't like apples,");
}

Now let's focus on a few more code snippets that use Array.prototype.filter() incorrectly and what can be used in their place. We will be using the following collection of Pets that I made up on the spot for the next few functions.


const pets = [{
  id: 1,
  name: "Soleil De Luna",
  type: "cat",
  gender: "f",
  age: 11,
  neutered: undefined,
  spayed: true
}, {
  id: 2,
  name: "Sissy Parsec, Goddess of Infinity, Trapped in the Body of a Hamster",
  type: "hamster",
  gender: "f",
  age: 2,
  neutered: undefined,
  spayed: false
}, {
  id: 3,
  name: "Elian \"Drogo\" Fifone",
  type: "cat",
  gender: "m",
  age: 10,
  neutered: true,
  spayed: undefined
}, {
  id: 4,
  name: "Nemesis Enforcer",
  type: "dog",
  gender: "m",
  age: 6,
  neutered: false,
  spayed: undefined
}, {
  id: 5,
  name: "Malichor the Undefeatable",
  type: "rock",
  gender: undefined,
  age: 44,
  neutered: undefined,
  spayed: undefined
}, {
  id: 6,
  name: "Quirty",
  type: "cat",
  gemder: "f",
  age: 14,
  neutered: undefined,
  spayed: false
}];

Now, I'm not saying that you can't ever use Array.prototype.filter() ever. As I said already, it has its uses, like when you want to get all neutered pets.


function getAllNeuteredPets() {
  return pets.filter(pet => pet.neutered);
}

What you shouldn't be using this method for, is for just fetching one pet. Or seeing if any of the pets in your collection have been spayed. Or to see if all of them are at least five years old. Each incorrect snippet has a corresponding correct snippet, and you can clearly tell what the function is supposed to do by the method names used.


// Incorrect
function getPetRockIncorrectly() {
  return pets.filter(pet => pet.type === "rock")[0];
}

// Correct
function getPetRock() {
  return pets.find(pet => pet.type === "rock");
}

// Incorrect
function hasSpayedPetsIncorrectly() {
  return pets.filter(pet => pet.spayed).length > 0;
}

// Correct
function hasSpayedPets() {
  return pets.some(pet => pet.spayed);
}

// Incorrect
function areAllPetsOverFiveYearsOldIncorrectly() {
  return pets.filter(pet => pet.age > 5).length === pets.length;
}

// Correct
function areAllPetsOverFiveYearsOld() {
  return pets.every(pet => pet.age > 5);
}

.find() is like .filter() except it only returns the first element in the array that matches the iterator function.

.some() is like .filter() except it returns a Boolean value; true if at least one element matches the iterator function.

.every() is like .some() except it only returns true if all elements match the iterator function.

You can read more about these functions and more about arrays at the MDN Web Docs, which I exclusively use as my JavaScript documentation. There are a ton more helpful instance methods that you can make use of as a developer. These are just some tips for me, to remind me to stop writing bad code.

Published: 12/08/2021 4:57pm

Comments

I'm just here for spacing.