JavaScript中的indexOf()方法

字符串(String)中的indexOf()方法

  • 语法

1
2
indexOf(searchString)
indexOf(searchString,position)
  • 参数

  1. searchString(必需),字符串类型,为要搜索的字符串。

注意被查找的字符串区分大小写。

假如没有提供该参数,searchString会被强制设置为"undefined"字符串,然后再当前字符串中查找这个值。

  1. position(可选),整数类型,即开始查找的地方,你也可以理解为类似数组中的下标。

若不提供参数,默认从0开始。

若提供的position的值小于0则默认从0开始查找。若提供的position的值大于原来字符串的长度,则该方法不搜索传入的searchString字符串,返回-1

如果参数中提供的索引值是一个负值,则整个字符串都将会被查询。

  • 返回值

该方法会返回查找字符串searchString的第一次出现的索引,如果没有找到,则返回-1

  • 注意当查找字符串是空字符串

若被查找的字符串searchString是一个空字符串,则返回position

1
2
3
'hello world'.indexOf('', 0) // 返回 0 查找的字符串searchString是一个空字符串,则返回position
'hello world'.indexOf('', 3) // 返回 3 查找的字符串searchString是一个空字符串,则返回position
'hello world'.indexOf('', 8) // 返回 8 查找的字符串searchString是一个空字符串,则返回position

​ 如果position值为空,或者position值小于被查找的字符串的长度,返回值和以下的position值一样。

1
2
3
'hello world'.indexOf('') // 返回 0 查找的字符串position值为空,则返回position
'hello world'.indexOf('', 1) // 返回 1 position的值小于字符串的长度11,则返回position
'hello world'.indexOf('', 2) // 返回 2 position的值小于字符串的长度11,则返回position

​ 如果position值大于等于字符串的长度,将会直接返回字符串的长度。

1
2
3
 'hello world'.indexOf('', 11) // 返回 11 position的值大于字符串的长度11,则返回字符串长度11
'hello world'.indexOf('', 13) // 返回 11 position的值大于字符串的长度11,则返回字符串长度11
'hello world'.indexOf('', 22) // 返回 11 position的值大于字符串的长度11,则返回字符串长度11
  • 常用场景

检测是否存在某字符串。

​ 当检查字符串中是否出现特定的子字符串时,正确的检查方法是测试返回值是否为 -1

1
2
'Blue Whale'.indexOf('Blue') !== -1  // true; found 'Blue' in 'Blue Whale'
'Blue Whale'.indexOf('Bloe') !== -1 // false; no 'Bloe' in 'Blue Whale'

使用 indexOf() 统计一个字符串中某个字母出现的次数

​ 在下例中,使用 count 来记录字母 e 在字符串 str 中出现的次数:

1
2
3
4
5
6
7
8
9
10
11
// 翻译:生存还是毁灭?这是个问题。(莎士比亚《哈姆雷特》)
const str = 'To be, or not to be, that is the question.';
let count = 0;
let position = str.indexOf('e');

while (position !== -1) {
count++;
position = str.indexOf('e', position + 1);
}

console.log(count); // displays 4

数组(Array)中的indexOf()方法

  • 语法

1
2
indexOf(searchElement)
indexOf(searchElement, fromIndex)
  • 参数

  1. searchElement(必需),需要查找的元素,类型不固定。

  2. fromIndex(可选),整数类型,开始查找的位置,数组中的下标。

若不提供参数,默认从0开始。

​ 开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回 -1。

​ 如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即 -1 表示从最后一个元素开始查找,-2 表示从倒数第 二个元素开始查找,以此类推。

  注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍       小于 0,则整个数组都将会被查询。其默认值为 0。
  • 返回值

​ 首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1

​ 注意:不提供searchElement该参数,或者提供空字符串,返回值都为-1。

  • 常用场景

找出指定元素出现的所有位置

1
2
3
4
5
6
7
8
9
10
const indices = [];
const array = ['a', 'b', 'a', 'c', 'a', 'd'];
const element = 'a';
let idx = array.indexOf(element);
while (idx !== -1) {
indices.push(idx);
idx = array.indexOf(element, idx + 1);
}
console.log(indices);
// [0, 2, 4]

判断一个元素是否在数组里,不在则更新数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function updateVegetablesCollection (veggies, veggie) {
if (veggies.indexOf(veggie) === -1) {
veggies.push(veggie);
console.log(`New veggies collection is: ${veggies}`);
} else {
console.log(`${veggie} already exists in the veggies collection.`);
}
}

const veggies = ['potato', 'tomato', 'chillies', 'green-pepper'];

updateVegetablesCollection(veggies, 'spinach');
// New veggies collection is: potato,tomato,chillies,green-pepper,spinach
updateVegetablesCollection(veggies, 'spinach');
// spinach already exists in the veggies collection.

字符串中的indexOf()方法与数组中的indexOf()方法的相同点和不同点

相同点

  • 都是用来查找某一元素的
  • 都有两个参数,要查找的元素,开始查找的位置
  • 查找字符串时严格区分大小写

不同点

  • 传要查找参数时,字符串的indexOf()方法会把不是字符串的参数,类型转换为字符串类型,数组不会。
  • 传入起始查找的参数时,字符串的参数不支持负数。数组支持负数,传负数时,会从最后一个元素下标开始抵消。
  • 返回值在某些情况下有所不同,字符串searchString参数为空字符串时,可能有多种返回结果。

JavaScript数组去重讲解详细

前言

博主也是刚刚开始水面试题才去了解JavaScript,并没有系统地去学习过JavaScript,如果博客或者其他博文有哪里不正确,不足的地方,欢迎各位指正、补充。话不多说,其实方法都一样(我就是加以自己的理解,也不知道对不对)例子均来自牛客面试题

1. 利用ES6 Set去重(ES6中最常用)

1
2
3
4
5
6
function unique (arr) {
return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]

解释

这是使用了ES6中新增的Array.from()和Set()来进行去重。

  1. 定义了一个函数unique,该函数接受一个数组作为参数。
  2. 使用Set数据结构来去重,将数组转为Set结构,再将Set结构转回数组。
  3. 返回去重后的数组。
  • Array.from() 方法用于将类数组对象( array-like object )可遍历对象( iterable object ) 转化为一个新的数组实例。
  • Set() 是ES6新增的数据结构,用于存储唯一的值,不允许重复。它类似于数组,但是成员的值都是唯一的。

因此,首先将输入的数组转化为一个Set数据结构(利用Set可以去除重复项),然后再将Set数据结构转化为数组返回给用户。

在这个例子中,输入的数组arr包含了不同数据类型的元素,包括数字、字符串、布尔值、undefined、null、以及对象等。其中NaN存在特别的问题,Set() 中的NaN值可以去重,但是两个NaN之间不相等,因此去重的结果中只会保留一个NaN。

不考虑兼容性,这种去重的方法代码最少。这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。

2. 利用for嵌套for,然后splice去重(ES5中最常用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique(arr){            
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了

解释

采用双重循环和splice方法,双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。

  1. 定义了一个函数unique,该函数接受一个数组作为参数。
  2. 使用双重循环来遍历数组中的每一个元素,并找到数组中的重复元素。
  3. 如果找到数组中的两个元素相同,那么就使用splice方法删除第二个元素,并将内层循环的计数器j减1,从而保证循环安全、完整。
  4. 返回去重后的数组。

在循环操作中,对于每一个元素,它会从它后面的元素开始逐个比较,如果发现有相同的元素,则使用splice方法将后面的元素删除,并且j自减1。因为splice方法的调用,这个嵌套的循环可能需要执行许多次,性能上会比Set方法慢一些。

然后函数返回去重后的数组。但是这个方法存在一些问题,比如:

  • NaN与对象一样,被认为是不同的元素类型,因此在此方法中不能移除掉数组中的所有NaN。
  • 原数组中有两个null,但是在去重后,这两个null却被消失了,这是因为splice方法在使用时,可能会引起数组索引的变更,会影响当前的循环操作。
  • 数组中的元素类型过于多样化时,并不容易保证去重的结果正确,比如在此例子中,两个{}对象并没有被移除,因为两个空对象之间是无法被“==”比较的。

3. 利用indexOf去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [1, "true", true, 15, false, undefined, null, NaN, NaN, "NaN", 0, "a", {…}, {…}] //NaN、{}没有去重

解释

  1. 定义了一个函数unique,该函数接受一个数组作为参数。
  2. 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
  3. 定义了一个变量array,用于存储去重后的数组。
  4. 使用for循环遍历传入的数组,取出数组中的每一个元素。
  5. 如果array数组中不存在该元素,则将该元素加入array数组中。
  6. 返回去重后的array数组。

然后函数返回去重后的数组。但是这个方法存在一些问题,比如:

  • 与第二种方法一样,这个方法也无法正确地去重NaN和{}对象。
  • 该方法的执行效率不如第一个方法Fast Set(ES6新增的Set)和第二个方法while+hasOwnProperty实现的快速去重。

4. 利用sort()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return;
}
arr = arr.sort()
var arrry= [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
arrry.push(arr[i]);
}
}
return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
// [0, 1, 15, "NaN", NaN, NaN, {…}, {…}, "a", false, null, true, "true", undefined] //NaN、{}没有去重

解释

  1. 定义了一个函数unique,该函数接受一个数组作为参数。
  2. 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
  3. 对传入的数组进行排序。
  4. 定义了两个变量arrryobj,分别用于存储去重后的数组和存储数组元素出现次数的对象。
  5. 将排序后的数组的第一个元素加入到arrry数组中。
  6. 使用for循环遍历排序后的传入的数组,取出数组中的每一个元素。
  7. 如果arr[i]arr[i-1]的值不相等,那么就将该元素加入到arrry数组中,实现去重。
  8. 最后返回去重后的数组arrry

然后函数返回去重后的数组。但是这个方法存在一些问题,比如:

  • 与前面三种方法一样,这个方法无法正确地去重包括NaN和{}对象在内的某些数据类型。在此例子中,NaN和{}对象没有被正确去重,而且它们还变得更多了(排序后相邻的NaN变成了3个,{}对象也变成了2个)。
  • 类型不同的数据进行排序后,得到的可能并不是想要的顺序,比如在此例子中,数字0出现在了末尾。

5. 利用对象的属性不能相同的特点进行去重(这种数组去重的方法有问题,不建议用,有待改进)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var arrry= [];
var obj = {};
for (var i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
arrry.push(arr[i])
obj[arr[i]] = 1
} else {
obj[arr[i]]++
}
}
return arrry;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, null, NaN, 0, "a", {…}] //两个true直接去掉了,NaN和{}去重

解释

  1. 检测传入的参数是否为数组,如果不是则打印出’type error’的错误信息,并结束函数的执行。
  2. 定义了两个变量arrryobj,分别用于存储去重后的数组和存储数组元素出现次数的对象。
  3. 使用for循环遍历传入的数组,取出数组中的每一个元素。
  4. 如果obj对象中未出现这个元素,即该元素没有出现过,就将它加入到arrry数组中,并给相应的obj属性赋初值1表示出现了一次。
  5. 如果obj对象中已经出现了该元素,那么就只将相应obj的属性值加1,表示该元素又出现了一次。
  6. 最后返回去重后的数组arrry

然后函数返回去重后的元素数组。但是这个方法也存在一些问题,比如:

  • 与前面所有方法一样,这个方法也无法正确地去重包括NaN和{}对象在内的某些数据类型。但是在此例子中,这个方法可以正确地去除所有重复元素。
  • 此方法可能会改变数组中元素的顺序,因为在循环遍历数组时,对象的属性遍历顺序并不是按照数组元素的出现顺序,因此去重结果有可能不是原数组中元素的原始顺序。

6. 利用includes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}] //{}没有去重

解释

  1. 创建一个函数 unique 并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 在函数内部,先检查传入的参数是否为数组,如果不是则打印一条类型错误的消息,并退出函数;否则继续下一步。
  3. 创建一个新的空数组 array,用于存储去重后的结果。
  4. 使用 for 循环遍历原始数组 arr,在遍历过程中对每个元素进行判断,如果 array 中不包含该元素,就将其添加到 array 数组中。
  5. 遍历完成后,返回 array 数组,其中包含了原始数组中的所有不重复元素。
  6. 注意,由于 includes 方法无法去重对象类型,所以在最终的结果中仍会保留数组中出现的对象值。

然后函数返回去重后的元素数组。但是这个方法也存在一些问题,比如:

  • 与前面所有方法一样,这个方法也无法正确地去重包括{}对象在内的某些数据类型。在此例子中,两个空对象没有被正确去除。
  • includes方法ES7新增,不是所有浏览器均支持这个方法。

7. 利用hasOwnProperty

1
2
3
4
5
6
7
8
9
function unique(arr) {
var obj = {};
return arr.filter(function(item, index, arr){
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}] //所有的都去重了

解释

  1. 创建一个函数 unique 并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 创建一个空对象 obj
  3. 使用 filter 函数来遍历数组。filter 函数对数组中所有元素执行给定的函数,返回一个新数组,该数组中的所有元素都是在测试函数中返回 true 的原始数组元素。
  4. filter 函数中,使用匿名函数来检查当前元素是否已经在原数组中出现。为了避免比较数据类型,使用 typeof 操作符获取其类型,并将其与元素值结合为一个字符串 typeof item + item
  5. 在每次遍历过程中,使用 hasOwnProperty 检查对象 obj 中是否已经存在当前元素。如果存在,则返回 false,不加入到结果数组中。
  6. 如果不存在,则将当前元素添加到 obj 对象中,并返回 true,将其加入到新数组中。
  7. 遍历完成后,返回已经去重的数组。

8. 利用filter

1
2
3
4
5
6
7
8
9
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, "NaN", 0, "a", {…}, {…}]

解释

  1. 创建一个函数 unique 并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 使用 filter 函数来遍历数组。filter 函数对数组中所有元素执行给定的函数,返回一个新数组,该数组中的所有元素都是在测试函数中返回 true 的原始数组元素。
  3. filter 函数中,使用匿名函数来检查当前元素是否已经在原数组中出现。使用 indexOf 方法查找当前元素在原始数组中的第一个索引,如果索引值与当前索引值相等,即表示当前元素是在原始数组中第一次出现,将其添加到结果数组中。
  4. 如果当前索引不是第一次出现,即与原始数组中其他索引值相等,则不添加到结果数组中。
  5. 遍历完成后,返回已经去重的数组。
  • 此方法在遍历数组的过程中会多次调用 indexOf 方法,又由于indexOf方法的时间复杂度为 O(n),因此其效率可能不够高。
  • 该方法可能不适用于处理元素为对象的数组,因为对象的引用地址不同,虽然对象具有相同的类型和值,但是 indexOf 方法不会被认为两个不同的对象是相同的元素,因此无法对它们去重。

9. 利用递归去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function unique(arr) {
var array= arr;
var len = array.length;

array.sort(function(a,b){ //排序后更加方便去重
return a - b;
})

function loop(index){
if(index >= 1){
if(array[index] === array[index-1]){
array.splice(index,1);
}
loop(index - 1); //递归loop,然后数组去重
}
}
loop(len-1);
return array;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

解释

  1. 创建一个函数 unique并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 创建一个新的变量 array 并将其值设置为传入参数的值。
  3. 创建一个变量 len 并将其赋值为 array 数组的长度。
  4. 使用 sort() 函数对 array 数组进行排序。这么做是为了更方便地进行数组去重。
  5. 函数中创建了一个名为 loop 的内部函数。loop 函数使用递归的方法遍历数组,比较前后两个元素是否相等,如果相等,则将后一个元素从数组中删除。
  6. 调用 loop(len-1) 函数,开始遍历 array 数组。
  7. 函数返回 array 数组(已经去除了重复元素)。

10. 利用Map数据结构去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function arrayNonRepeatfy(arr) {
let map = new Map();
let array = new Array(); // 数组用于返回结果
for (let i = 0; i < arr.length; i++) {
if(map .has(arr[i])) { // 如果有该key值
map .set(arr[i], true);
} else {
map .set(arr[i], false); // 如果没有该key值
array .push(arr[i]);
}
}
return array ;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "a", "true", true, 15, false, 1, {…}, null, NaN, NaN, "NaN", 0, "a", {…}, undefined]

解释

  1. 创建一个函数 arrayNonRepeatfy 并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 创建一个新的 Map 对象 map,用于存储数组中的值。
  3. 创建一个新的数组 array,用于存储去重后的结果。
  4. 使用 for 循环遍历数组 arr。在遍历过程中,获取当前元素的值。使用 Map 的 has() 方法检查该元素是否已经存在于 Map 中。
  5. 如果存在,将该元素对应的 Map 的 value 值设为 true。
  6. 如果不存在,则将该元素加入到数组 array 中,并将 Map 的 value 值设为 false。
  7. 遍历完成后,返回已经去重的数组 array

11. 利用reduce+includes

1
2
3
4
5
6
function unique(arr){
return arr.reduce((prev,cur) => prev.includes(cur) ? prev : [...prev,cur],[]);
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr));
// [1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {…}, {…}]

解释

  1. 创建一个函数 unique 并传入一个参数 arr,这个参数 arr 是需要去重的数组。
  2. 使用 reduce 函数来遍历数组。reduce 函数将数组元素传递给指定的函数,返回一个输出结果。初始值为空数组[]。
  3. reduce 函数中,使用箭头函数处理每个元素。如果这个元素没有在初始值数组 prev 中出现过,那么就将这个元素放入数组 prev 中,使用逗号运算符返回新数组[…prev,cur]。
  4. 如果元素已经出现在 prev 数组中,那么就返回之前的 prev 数组,从而达到去重的目的,即 return prev。
  5. 遍历完成后,返回已经去重的数组 prev

12. […new Set(arr)]

1
[...new Set(arr)] 

解释

  1. 在 ES6 中,Set 是一种新的数据结构,表示一组无序且唯一的值。Set 可以接收一个可迭代对象,比如数组,在迭代对象中进行去重操作。
  2. 将数组 arr 传递给 Set 构造函数,它会自动去除数组中的重复项。
  3. 使用扩展运算符[...]将 Set 转换成数组,从而实现去重。
  4. 返回已经去重的数组。