JS查找数组指定元素

查找数组指定元素

ES6 findIndex

findIndex它会返回数组中满足提供的函数的第一个元素的索引,否则返回 -1

    let arr = [1,15,15,45,24,37,298,111,15]
    arr.findIndex(isBigEnough)

    isBigEnough(element){
        return element > 200
    },

模拟实现findIndex

    findIndex(arr,func,context){
        for(let i =0;i

模拟实现findLastIndex

跟findIndex差不多,只不过是倒序查找

    findLastIndex(arr,func,context){
        var length = arr.length
        for(let i = length-1 ;i>=0;i--){
            if(func.call(context,arr[i])) return i
        }
        return -1
    },

createIndexFinder

观察可以知道findIndex 和findLastIndex的实现原理基本相同,区别只在于查找顺讯,那么如何通过实现一个函数,通过不同的传参来判断是使用findIndex还是findLastIndex呢?

实现:

    createIndexFinder(dir){
        return function(arr,func,context){
            let length = arr.length;
            let index = dir > 0 ? 0 : length -1
            for(;index >=0 && index < length; index+=dir){
                if(func.call(context,arr[index])) return index
            }
            return -1
        }
    },

sortedIndex

新的需求:如何在一个排好序的数组中找到 value 对应的位置,保证插入数组后,依然保持有序的状态?

比如:sortedIndex([10, 20, 30], 25); // 2

如果是有序数组,那我们就不采用遍历的形式,采用二分法

    sortedIndex(arr,target){
        let low = 0,high = arr.length;
        while(low

看上去不错,基本实现了我们的要求,但还不够好,如果我想实现下面这种情况要怎么处理?

// stooges 配角 比如 三个臭皮匠 The Three Stooges
var stooges = [{name: 'stooge1', age: 10}, {name: 'stooge2', age: 30}];

var result = sortedIndex(stooges, {name: 'stooge3', age: 20}, function(stooge){
    return stooge.age
});

console.log(result) // 1

进阶实现:

    sortedIndex(arr,target,iteratee,context){
        iteratee = this.cb(iteratee,context)
        let low = 0,high = arr.length;
        while(low < high){
            let mid = Math.floor((low + high) / 2)
            if(iteratee(arr[mid]) < iteratee(target)){
                low = mid + 1
            }else{
                high = mid
            }
        }
        return high
    },
    // 处理函数this问题
    cb(func,context){
        if(context === void 0){
            return func
        }else{
            return func.apply(context,arguments)
        }
    },

indexOf/lastIndexOf

现在尝试手写一个indexOf/lastIndexOf

    createIndexOfFinder(dir){
        return function(arr,item){
            let length = arr.length
            let index = dir > 0 ? 0 : length - 1
            for(;index>= 0 && index

indexOf和lastIndexOf都支持第二个参数fromIndex表示开始查找的位置。

在MDN上对fromIndex的解释如下:

  • indexOf:

fromIndex

开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.

比如:

    var array = [2, 5, 9,11,13];
    array.indexOf(2, 3); // -1  表示从第三个位置开始向后查找
    array.indexOf(5, -4) // 1  -4表示从倒数第四个位置开始向后查找
  • lastIndexOf

fromIndex

从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

    var array = [2, 5, 9,2,11,13];
    array.lastIndexOf(2,2) // 0 从第二个位置向前查找 
    array.lastIndexOf(2,-3) // 3 从后向前数三个位置开始向前查找
    

根据以上规则,我们实现第二版

    createIndexOfFinder(dir){
        return function(arr,item,idx){
            let length = arr.length
            let i=0
            if(typeof idx == 'number'){
                if(dir > 0){
                    i = idx > 0 ? idx : Math.max(idx,length)
                }else{
                    length = idx > 0 ? Math.min(idx+1,length) : idx+length+1
                }
            }
            for(idx = dir > 0 ? i : length - 1;idx>=0 && idx

优化

主要围绕下面两点进行

  1. 如何支持查找NaN
  2. 支持对有序数组更快的二分查找,如果formIndex传一个true就表示数组有序

根据以上要求,看下最终实现方法

function createIndexOfFinder(dir, predicate, sortedIndex) {

    return function(array, item, idx){
        var length = array.length;
        var i = 0;

        if (typeof idx == "number") {
            if (dir > 0) {
                i = idx >= 0 ? idx : Math.max(length + idx, 0);
            }
            else {
                length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
            }
        }
        else if (sortedIndex && idx && length) {
            idx = sortedIndex(array, item);
            // 如果该插入的位置的值正好等于元素的值,说明是第一个符合要求的值
            return array[idx] === item ? idx : -1;
        }

        // 判断是否是 NaN
        if (item !== item) {
            idx = predicate(array.slice(i, length), isNaN)
            return idx >= 0 ? idx + i: -1;
        }

        for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
            if (array[idx] === item) return idx;
        }
        return -1;
    }
}

var indexOf = createIndexOfFinder(1, findIndex, sortedIndex);
var lastIndexOf = createIndexOfFinder(-1, findLastIndex);
在 underscore 的实现中,只有 indexOf 是支持有序数组使用二分查找,lastIndexOf 并不支持

你可能感兴趣的:(JS查找数组指定元素)