JavaScript是当今流行语言中对函数式编程支持最好的编程语言。我们继续构建函数式编程的基础,在前文中分解介绍了对array数组作变形transform四种方法,分别为:
- array.reduce 帮你精通JS:神奇的array.reduce方法的10个案例
- array.map 帮你精通JS:神奇的array.map的6个案例
- array.flat and array.flatMap 帮你精通JS: array.flat与flatMap用法指南
为什么一种编程语言要耗费如此巨大的精力在数组这种数据结构上呢?因为数组是我们日常思考的基石。比如你早起筹划的一天的任务是数组,你的购物清单是数组。
学习编程,绝不能将纸上的字符简简单单的只当作抽象的无意义的符号,我们要还原为,具象为具体的生活与应用之中。
接下来,我们继续构建我们的思维。介绍用于逻辑判断的array.filter, array.find, array.findIndex以及array.include(array.find应用于具体个例)和array.indexOf(array.findInde用于具体个例) 和array.some, array.every.
1.1 array.filter() 概述
filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
语法
- var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
参数
callback 用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:
- element 数组中当前正在处理的元素。
- index可选 正在处理的元素在数组中的索引。
- array可选 调用了 filter 的数组本身。
thisArg可选 执行 callback 时,用于 this 的值。
返回值
一个新的、由通过测试的元素组成的数组,如果没有任何数组元素通过测试,则返回空数组。
1.2 array.filter() 描述
filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或等价于 true 的值的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。
callback 被调用时传入三个参数:
- 元素的值
- 元素的索引
- 被遍历的数组本身
如果为 filter 提供一个 thisArg 参数,则它会被作为 callback 被调用时的 this 值。否则,callback 的 this 值在非严格模式下将是全局对象,严格模式下为 undefined。callback 函数最终观察到的 this 值是根据通常函数所看到的 "this"的规则确定的。
filter 不会改变原数组,它返回过滤后的新数组。
filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。
案例 01 筛选排除所有较小的值
下例使用 filter 创建了一个新数组,该数组的元素由原数组中值大于 10 的元素组成。
- function isBigEnough(element) {
- return element >= 10;
- }
- var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
- // filtered is [12, 130, 44]
案例 02 过滤 JSON 中的无效条目
以下示例使用 filter() 创建具有非零 id 的元素的 json。
- var arr = [
- { id: 15 },
- { id: -1 },
- { id: 0 },
- { id: 3 },
- { id: 12.2 },
- { },
- { id: null },
- { id: NaN },
- { id: 'undefined' }
- ];
-
- var invalidEntries = 0;
-
- function isNumber(obj) {
- return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
- }
-
- function filterByID(item) {
- if (isNumber(item.id) && item.id !== 0) {
- return true;
- }
- invalidEntries++;
- return false;
- }
-
- var arrByID = arr.filter(filterByID);
-
- console.log('Filtered Array\n', arrByID);
- // Filtered Array
- // [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]
-
- console.log('Number of Invalid Entries = ', invalidEntries);
- // Number of Invalid Entries = 5
案例 03 在数组中搜索
下例使用 filter() 根据搜索条件来过滤数组内容。
- var fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
-
-
- function filterItems(query) {
- return fruits.filter(function(el) {
- return el.toLowerCase().indexOf(query.toLowerCase()) > -1;
- })
- }
-
- console.log(filterItems('ap')); // ['apple', 'grapes']
- console.log(filterItems('an')); // ['banana', 'mango', 'orange']
案例 04 ES2015 实现
- const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
-
-
- const filterItems = (query) => {
- return fruits.filter((el) =>
- el.toLowerCase().indexOf(query.toLowerCase()) > -1
- );
- }
-
- console.log(filterItems('ap')); // ['apple', 'grapes']
- console.log(filterItems('an')); // ['banana', 'mango', 'orang
2.1 array.find() 概述
find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
- const array1 = [5, 12, 8, 130, 44];
-
- const found = array1.find(element => element > 10);
-
- console.log(found);
- // expected output: 12
另请参见 3.1 findIndex() 方法,它返回数组中找到的元素的索引,而不是其值。
如果你需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。
语法
- arr.find(callback[, thisArg])
参数
callback 在数组每一项上执行的函数,接收 3 个参数:
- element 当前遍历到的元素。
- index可选 当前遍历到的索引。
- array可选 数组本身。
thisArg可选 执行回调时用作this 的对象。
返回值
数组中第一个满足所提供测试函数的元素的值,否则返回 undefined。
2.2 array.find() 描述
find方法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true。当找到了这样一个元素后,该方法会立即返回这个元素的值,否则返回 undefined。注意 callback 函数会为数组中的每个索引调用即从 0 到 length - 1,而不仅仅是那些被赋值的索引,这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。
callback函数带有3个参数:当前元素的值、当前元素的索引,以及数组本身。
如果提供了 thisArg参数,那么它将作为每次 callback函数执行时的this ,如果未提供,则使用 undefined。
find方法不会改变数组。
在第一次调用 callback函数时会确定元素的索引范围,因此在 find方法开始执行之后添加到数组的新元素将不会被 callback函数访问到。如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍旧会被访问到,但是其值已经是undefined了。
案例 01 用对象的属性查找数组里的对象
- var inventory = [
- {name: 'apples', quantity: 2},
- {name: 'bananas', quantity: 0},
- {name: 'cherries', quantity: 5}
- ];
-
- function findCherries(fruit) {
- return fruit.name === 'cherries';
- }
-
- console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
案例 02 寻找数组中的质数
下面的例子展示了如何从一个数组中寻找质数(如果找不到质数则返回undefined)
- function isPrime(element, index, array) {
- var start = 2;
- while (start <= Math.sqrt(element)) {
- if (element % start++ < 1) {
- return false;
- }
- }
- return element > 1;
- }
-
- console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
- console.log([4, 5, 8, 12].find(isPrime)); // 5
当在回调中删除数组中的一个值时,当访问到这个位置时,其传入的值是 undefined:
- // Declare array with no element at index 2, 3 and 4
- var a = [0,1,,,,5,6];
-
- // Shows all indexes, not just those that have been assigned values
- a.find(function(value, index) {
- console.log('Visited index ' + index + ' with value ' + value);
- });
-
- // Shows all indexes, including deleted
- a.find(function(value, index) {
-
- // Delete element 5 on first iteration
- if (index == 0) {
- console.log('Deleting a[5] with value ' + a[5]);
- delete a[5]; // 注:这里只是将a[5]设置为undefined,可以试试用a.pop()删除最后一项,依然会遍历到被删的那一项
- }
- // Element 5 is still visited even though deleted
- console.log('Visited index ' + index + ' with value ' + value);
- });
3.1 array.findIndex() 概述
findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1。
- var inventory = [
- {name: 'apples', quantity: 2},
- {name: 'bananas', quantity: 0},
- {name: 'cherries', quantity: 5}
- ];
-
- function findCherries(fruit) {
- return fruit.name === 'cherries';
- }
-
- console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
另请参见 1.1 find() 方法,它返回数组中找到的元素的值,而不是其索引。
语法
- arr.findIndex(callback[, thisArg])
参数
callback 针对数组中的每个元素, 都会执行该回调函数, 执行时会自动传入下面三个参数:
- element 当前元素。
- index 当前元素的索引。
- array 调用findIndex的数组。
thisArg 可选。执行callback时作为this对象的值.
返回值
数组中通过提供测试函数的第一个元素的索引。否则,返回-1
3.2 array.findIndex() 描述
findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。如果找到这样的元素,findIndex会立即返回该元素的索引。如果回调从不返回真值,或者数组的length为0,则findIndex返回-1。 与某些其他数组方法(如Array#some)不同,在稀疏数组中,即使对于数组中不存在的条目的索引也会调用回调函数。
回调函数调用时有三个参数:元素的值,元素的索引,以及被遍历的数组。
如果一个 thisArg 参数被提供给 findIndex, 它将会被当作this使用在每次回调函数被调用的时候。如果没有被提供,将会使用undefined。
findIndex不会修改所调用的数组。
在第一次调用callback函数时会确定元素的索引范围,因此在findIndex方法开始执行之后添加到数组的新元素将不会被callback函数访问到。如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍然会被访问到。
案例 01 查找数组中首个质数元素的索引
以下示例查找数组中素数的元素的索引(如果不存在素数,则返回-1)。
- function isPrime(element, index, array) {
- var start = 2;
- while (start <= Math.sqrt(element)) {
- if (element % start++ < 1) {
- return false;
- }
- }
- return element > 1;
- }
-
- console.log([4, 6, 8, 12].find(isPrime)); // undefined, not found
- console.log([4, 5, 8, 12].find(isPrime)); // 5
4.1 array.includes() 概述
当2.1的array.find查找具体的元素的时候,就是array.include。
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
语法
- arr.includes(valueToFind[, fromIndex])
参数
valueToFind
需要查找的元素值。
Note: 使用 includes()比较字符串和字符时是区分大小写。
fromIndex 可选 从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。
返回值
返回一个布尔值 Boolean ,如果在数组中找到了(如果传入了 fromIndex ,表示在 fromIndex 指定的索引范围中找到了)则返回 true 。
案例 00 简单示例
- [1, 2, 3].includes(2); // true
- [1, 2, 3].includes(4); // false
- [1, 2, 3].includes(3, 3); // false
- [1, 2, 3].includes(3, -1); // true
- [1, 2, NaN].includes(NaN); // true
案例 01 fromIndex 大于等于数组长度
如果 fromIndex 大于等于数组的长度,则会返回 false,且该数组不会被搜索。
- var arr = ['a', 'b', 'c'];
-
- arr.includes('c', 3); // false
- arr.includes('c', 100); // false
案例 02 计算出的索引小于 0
如果 fromIndex 为负值,计算出的索引将作为开始搜索searchElement的位置。如果计算出的索引小于 0,则整个数组都会被搜索。
- // array length is 3
- // fromIndex is -100
- // computed index is 3 + (-100) = -97
-
- var arr = ['a', 'b', 'c'];
-
- arr.includes('a', -100); // true
- arr.includes('b', -100); // true
- arr.includes('c', -100); // true
- arr.includes('a', -2); // false
案例 03 作为通用方法的 includes()
includes() 方法有意设计为通用方法。它不要求this值是数组对象,所以它可以被用于其他类型的对象 (比如类数组对象)。下面的例子展示了 在函数的 arguments 对象上调用的 includes() 方法。
- (function() {
- console.log([].includes.call(arguments, 'a')); // true
- console.log([].includes.call(arguments, 'd')); // false
- })('a','b','c');
5.1 array.indexOf() 概述
indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
语法
- arr.indexOf(searchElement[, fromIndex])
参数
- searchElement
要查找的元素
- fromIndex 可选
开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.
返回值
首个被找到的元素在数组中的索引位置; 若没有找到则返回 -1
5.2 array.indexOf() 描述
indexOf 使用strict equality (无论是 ===, 还是 triple-equals操作符都基于同样的方法)进行判断 searchElement与数组中包含的元素之间的关系。
案例 01 使用indexOf
以下例子使用indexOf方法确定多个值在数组中的位置。
- var array = [2, 5, 9];
- array.indexOf(2); // 0
- array.indexOf(7); // -1
- array.indexOf(9, 2); // 2
- array.indexOf(2, -1); // -1
- array.indexOf(2, -3); // 0
案例 02 找出指定元素出现的所有位置
- var indices = [];
- var array = ['a', 'b', 'a', 'c', 'a', 'd'];
- var element = 'a';
- var idx = array.indexOf(element);
- while (idx != -1) {
- indices.push(idx);
- idx = array.indexOf(element, idx + 1);
- }
- console.log(indices);
- // [0, 2, 4]
案例 03 判断一个元素是否在数组里,不在则更新数组
- function updateVegetablesCollection (veggies, veggie) {
- if (veggies.indexOf(veggie) === -1) {
- veggies.push(veggie);
- console.log('New veggies collection is : ' + veggies);
- } else if (veggies.indexOf(veggie) > -1) {
- console.log(veggie + ' already exists in the veggies collection.');
- }
- }
-
- var veggies = ['potato', 'tomato', 'chillies', 'green-pepper'];
-
- // New veggies collection is : potato,tomato,chillies,green-papper,spinach
- updateVegetablesCollection(veggies, 'spinach');
- // spinach already exists in the veggies collection.
- updateVegetablesCollection(veggies, 'spinach');
6.总结
以上介绍了array用作高阶逻辑判断的5个函数:
array.filter, array.find, array,findIndex, array.includes, array.indexOf
另外还有比较常用但是较为简单的array.every与array.some也用作逻辑判断,此处略去不提。