es6 的 findIndex
es6 对数组新增了 findIndex 方法,返回数组中满足提供的函数的第一个元素的索引,否则返回 -1。
例子
function isBigEnough(element) {return element >= 15;}[12, 5, 8, 130, 44].findIndex(isBigEnough); // 3
实现 findIndex
遍历一次,返回符合条件的第一个元素的索引值。
function findIndex(array, predicate, context) {for (let i = 0; i < array.length; i++) {if (predicate.call(context, array[i], i, array)) return i;}return -1;}
实现 findLastIndex
findIndex 是正序,那怎样实现倒序查找了?修改一下循环就可以了。
function findLastIndex(array, predicate, context) {let length = array.length;for (let i = length - 1; i >= 0; i--) {if (predicate.call(context, array[i], i, array)) return i;}return -1;}console.log(findLastIndex([1, 2, 3, 4, 5, 6, 6, 6, 6], function (item, index, array) {if (item === 6) return true;})); // 8
合并 findIndex 和 findLastIndex
上面的两个实现,代码重复太多了,那怎样合并到一起了?很简单利用传参的不同,返回不同的函数。
// 根据传参的不同返回不同的函数function createIndexFinder(dir) {return function (array, predicate, context) {let length = array.length;let index = dir > 0 ? 0 : length - 1;// index >= 0 是倒序的临界条件;index < length 是正序的临界条件for (; index >= 0 && index < length; index += dir) {if (predicate.call(context, array[index], index, array)) return index;}return -1;};}const findIndex = createIndexFinder(1);const findLastIndex = createIndexFinder(-1);
sortedIndex
现在有个新需求:在一个排好序的数组中找到 value 对应的位置,保证插入到数组后,依然是有序的。
例子(假设有个叫 sortedIndex 的函数)
sortedIndex([10, 20, 30], 25) // 2
数组变成 [10, 20, 25, 30] 依然是有序的。
可以利用二分法来实现。
function sortedIndex(array, obj) {let low = 0, high = array.length;while (low < high) {let mid = Math.floor((low + high) / 2);if (array[mid] < obj) low = mid + 1;else high = mid;}return high;}console.log(sortedIndex([10, 20, 30, 40, 50], 35)); // 3
支持更多情况,比如添加一个判断函数。
function cb(func, context) {if (context === void 0) return func;return function () {return func.apply(context, arguments);};}function sortedIndexTwo(array, obj, iteratee, context) {iteratee = cb(iteratee, context);let low = 0, high = array.length;while (low < high) {let mid = Math.floor((low + high) / 2);if (iteratee(array[mid]) < iteratee(obj)) low = mid + 1;else high = mid;}return high;}const stooges = [{name: 'stooge1', age: 10}, {name: 'stooge2', age: 30}];const result = sortedIndexTwo(stooges, {name: 'stooge3', age: 20}, function(stooge){return stooge.age});console.log(result) // 1
实现 indexOf 和 lastIndexOf
也很简单,直接遍历。
function createIndexOfFinder(dir) {return function (array, item) {let length = array.length;let index = dir > 0 ? 0 : length - 1;for (; index >= 0 && index < length; index += dir) {if (array[index] === item) return index;}return -1;};}const indexOf = createIndexOfFinder(1);const lastIndexOf = createIndexOfFinder(-1);const result = indexOf([1, 2, 3, 4, 5], 2);console.log(result); // 1
indexOf 和 lastIndexOf 都支持 fromIndex 参数,来看看 mdn 是怎样解释的。
indexOf
开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0。
fromIndex
从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。
实现一下
function createIndexOfFinder(dir) {return function (array, item, idx) {let length = array.length;let i = 0;if (typeof idx == "number") {if (dir > 0) {// 正序i = idx >= 0 ? idx : Math.max(length + idx, 0);} else {// 倒序// 这里的 + 1 是兼容下面 for 循环中第二个 length。// 这里的 + 1 在下面的 for 中第一个 length 也减去。length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;}}for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {if (array[idx] === item) return idx;}return -1;};}
支持查找 NaN
function createIndexOfFinder(dir, predicate) {return function(array, item, idx){if () { ... };// 判断元素是否是 NaNif (item !== item) {// 在截取好的数组中查找第一个满足isNaN函数的元素的下标idx = predicate(array.slice(i, length), isNaN)return idx >= 0 ? idx + i: -1;}for () { ... };}}const indexOf = createIndexOfFinder(1, findIndex);const lastIndexOf = createIndexOfFinder(-1, findLastIndex);
对有序的数组使用二分查找。
如果 indexOf 的第三个参数不传开始搜索的下标值,而是一个 true,就认为数组是一个排好序的数组。就使用二分查找。我们可以利用 sortedIndex。
注意:倒序不支持。
最终代码
function createIndexOfFinder(dir, predicate, sortedIndex) {return function (array, item, idx) {let length = array.length;let i = 0;if (typeof idx == "number") {if (dir > 0) {// 正序i = idx >= 0 ? idx : Math.max(length + idx, 0);} else {// 倒序// 这里的 + 1 是兼容下面 for 循环中第二个 length。// 这里的 + 1 在下面的 for 中第一个 length 也减去。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;}// 判断是否是 NaNif (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;};}const indexOf = createIndexOfFinder(1, findIndex, sortedIndex);const lastIndexOf = createIndexOfFinder(-1, findLastIndex);console.log(indexOf([NaN, 1, 3, 4], NaN));
参考:
[1] JavaScript专题之学underscore在数组中查找指定元素
[2] indexOf
[3] lastIndexOf
[4] findIndex
[5] isNaN
