In js development, we often encounter the need to determine whether a certain element exists in an array. In fact, there are many ways to determine this, so let’s understand them one by one.

Let’s define an array first.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const arr = [
  13,
  false,
  'abcd',
  undefined,
  13,
  null,
  NaN,
  [1, 2],
  { a: 123 },
  () => Date.now(),
  new Date('2021/03/04'),
  new RegExp('abc', 'ig'),
  Symbol('sym'),
];

In this array, we have several types: number, boolean, string, undefined, null, array, object, Date, Symbol, etc. The number 13 appears 2 times.

1. indexOf

We are most familiar with indexOf, after all, it appeared early, it is compatible, and it is easy to use.

If the element exists, the index value of the first occurrence is returned; if the element does not exist in the whole array, -1 is returned.

1.1 Usage

Just determine if the returned data is -1 to know if the array contains the element.

1
2
arr.indexOf(13) >= 0; // true, indexOf返回0
arr.indexOf(2) >= 0; // false, indexOf返回-1

The counterpart of indexOf is lastIndexOf, which looks for the element from last to first and returns the index of the last one in the array if it exists, or -1 if it does not exist.

1
arr.lastIndexOf(13) >= 0; // true, lastIndexOf返回4, 最后一次出现的索引

Both methods are called in the same way when determining whether a variable exists or not.

1.2 2nd optional parameter

indexOf and lastIndexOf have a second optional parameter, fromIndex, which indicates the index from which the search is performed.

In indexOf, if fromIndex exceeds the length of the array, -1 is returned directly, and if it is negative, the search starts at the last index (arr.length-Math.abs(fromIndex)) and goes backwards.

In lastIndexOf, if fromIndex reaches or exceeds the length of the array, the whole array is searched; if it is a negative number, several indexes are counted from the end to the front (arr.length-Math.abs(fromIndex)), and then the search starts from the front, and if the absolute value of the negative number exceeds the length of the array, -1 is returned directly.

1
2
3
4
arr.indexOf(13, 2); // 4, 从索引值2开始往后查找,首先找到的13的索引值为4
arr.indexOf(13, -10); // 4, 从索引值1(11-10)开始往后检索
arr.lastIndexOf(13, 2); // 0, 从索引值2往前开始搜索
arr.lastIndexOf(13, -2); // 4, 从索引值9(11-2)开始往前搜索

And the indexOf and lastIndexOf are determined in a strictly equal way (===).

1
arr.indexOf(null); // 5, 在null的前面有几个假值false和undefined,也能准确找到null的索引值

2. includes

indexOf is mainly to find the index value of the element, but we can use the returned index value to indirectly determine if the element exists in the array.

The includes method, added in ES7 (ES2016), is specifically designed to determine whether an element exists or not. The return value is true or false, true means it exists, false means it does not exist, simple and clear.

1
2
3
arr.includes(13); // true
arr.includes('abc'); // false
arr.includes(false); // true, 存在false元素

Also, there is a second optional argument to the includes method, fromIndex, which is used in the same way as in indexOf. If fromIndex exceeds the length of the array, -1 is returned directly, and if it is negative, the index is counted from the end to the first (arr.length-Math.abs(fromIndex)), and then the search starts backwards.

1
arr.includes(13, 5); // false, 从索引值5开始往后检索,没检索到

So far, we have not judged the latter types, such as Array, Object, Date and Symbol. Let’s now look at the latter elements.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 使用indexOf判断
arr.indexOf(NaN); // -1
arr.indexOf([1, 2]); // -1
arr.indexOf({ a: 123 }); // -1
arr.indexOf(() => Date.now()); // -1
arr.indexOf(new Date('2021/03/04')); // -1
arr.indexOf(new RegExp('abc', 'ig')); // -1
arr.indexOf(Symbol('sym')); // -1

// 使用includes判断
arr.includes(NaN); // false
arr.includes([1, 2]); // false
arr.includes({ a: 123 }); // false
arr.includes(() => Date.now()); // false
arr.includes(new Date('2021/03/04')); // false
arr.includes(new RegExp('abc', 'ig')); // false
arr.includes(Symbol('sym')); // false

The result is tragic, none of these elements are retrieved in the array. But in fact, they are all true in the array.

This is because both indexOf and includes are determined in a strict equality way (===).

1
2
3
4
5
NaN === NaN; // false, 两个NaN永远也不会相等
[1, 2] === [1, 2]; // false, 每个声明出来的数组都有单独的存储地址
{a: 123} === {a: 123}; // false, 同数组
new Date('2021/03/04')===new Date('2021/03/04'); // false, 看着日期是相同的,但是用new出来的对象进行比较的,肯定是不相等的
Symbol('sym')===Symbol('sym'); // Symbol类型的出现就是为了避免冲突创造出来的类型,括号里的属性仅是为了方便描述而已

For these types that cannot be retrieved, we will need to write our own functions to determine the special types.

3. find and findIndex

find() and findIndex() allow us to customize the way we judge through callback functions.

3.1 The find method

The find() method returns the value of the first element of the array that satisfies the test function provided. Otherwise it returns undefined.

The find() method cannot detect undefined elements in an array.

Because the find() method returns undefined for both the absence and the presence of undefined elements, we will have to consider other approaches, which we will discuss later.

1
2
3
arr.find((item) => item === 13); // 13, 找到了元素13
arr.find((item) => item === 3); // undefined, 没找到元素3
arr.find((item) => item === undefined); // undefined, 也不知道是找到了还是没找到

For the slightly more complex types above, we need special judgments.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
arr.find((item) => typeof item === 'number' && isNaN(item)); // NaN

// array和object类型进行比较时,情况很复杂,因为每个元素的类型都无法确定
// 如果确定都是基本类型,如string, number, boolean, undefined, null等,可以将其转为字符串再比较
// 转字符串的方式也很多,如JSON.stringify(arr), arr.toString(), arr.split('|')等
// 复杂点的,只能一项一项比较,或者使用递归
arr.find((item) => item.toString() === [1, 2].toString()); // [1, 2]
arr.find((item) => JSON.stringify(item) === JSON.stringify({ a: 123 })); // {a: 123}
arr.find((item) => {
  if (typeof item === 'function') {
    return item.toString() === (() => Date.now()).toString();
  }
  return false;
}); // () => Date.now()
arr.find((item) => {
  if (item instanceof Date) {
    return item.toString() === new Date('2021/03/04').toString();
  }
  return false;
}); // Thu Mar 04 2021 00:00:00 GMT+0800

The above judgment code will be used later in the method as well.

3.2 Comparing two elements

We have compared multiple types of elements above, so let’s summarize a little.

First, let’s define a function.

1
const compare = (x, y) => {};

3.2.1 Basic types

For elements of basic types such as string, number, boolean, undefined, null, etc., direct comparison is possible.

1
2
3
const compare = (x, y) => {
  return x === y;
};

3.2.2 NaN data

NaN is determined to be of type number using typeof, but NaN is not equal to any number, including itself.

1
2
3
4
5
6
const compare = (x, y) => {
  if (typeof x === 'number' && isNaN(x) && typeof y === 'number' && isNaN(y)) {
    return true;
  }
  return x === y;
};

3.2.3 Function and Date and RegExp

These types can be compared by converting variables to strings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const compare = (x, y) => {
  if (typeof x === 'number' && isNaN(x) && typeof y === 'number' && isNaN(y)) {
    return true;
  }
  if (
    (typeof x === 'function' && typeof y === 'function') ||
    (x instanceof Date && y instanceof Date) ||
    (x instanceof RegExp && y instanceof RegExp) ||
    (x instanceof String && y instanceof String) ||
    (x instanceof Number && y instanceof Number)
  ) {
    return x.toString() === y.toString();
  }
  return x === y;
};

For object types and arrays, we can split each item and then compare them one by one using the above method.

3.3 The findIndex method

If we want to determine if there is an undefined array, we can use the findIndex() method.

The findIndex() method returns the index of the first element of the array that satisfies the provided test function. If the corresponding element is not found then -1 is returned.

1
2
arr.findIndex((item) => item === undefined); // 3
arr.findIndex((item) => item === 3); // -1, 没有找到数字3

The other data formats are determined as in find() above.

4. some

The some() method tests if at least 1 element of the array passes the provided function test. It returns a value of type Boolean.

Note: If you test with an empty array, it will return false in any case.

The some() method is used in the same way as the find() method, except that the some() method returns data of type boolean.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
arr.some((item) => item === false); // true
arr.some((item) => item === undefined); // true
arr.some((item) => typeof item === 'number' && isNaN(item)); // true
arr.some((item) => item === 3); // false, 不存在数字3
arr.some((item) => {
  if (item instanceof Date) {
    return item.toString() === new Date('2021/03/04').toString();
  }
  return false;
}); // true

5. filter

The filter() method creates a new array containing all the elements of the test implemented by the provided function.

Whether several elements are found or none, the filter() method returns an array whose data is the elements we want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
arr.filter((item) => item === false); // 1
arr.filter((item) => item === undefined); // 1
arr.filter((item) => typeof item === 'number' && isNaN(item)); // 1
arr.filter((item) => item === 13); // 2
arr.filter((item) => item === 3); // 0
arr.filter((item) => {
  if (item instanceof Date) {
    return item.toString() === new Date('2021/03/04').toString();
  }
  return false;
}); // 1

So we can determine whether the original array contains the elements we want by the length of that array.

6. Summary

There are many ways to find elements in an array, we can choose a more suitable way by the format of the elements in the array. If they are basic types, we recommend using the includes() method; if the format is more complex, we recommend using the some() method. Both methods return the boolean type directly, so you can use the result of the method directly without more conversion.