幫你精通JS:函數(shù)式array邏輯判斷的7個高階函數(shù)
JavaScript是當(dāng)今流行語言中對函數(shù)式編程支持最好的編程語言。我們繼續(xù)構(gòu)建函數(shù)式編程的基礎(chǔ),在前文中分解介紹了對array數(shù)組作變形transform四種方法,分別為:
- array.reduce 幫你精通JS:神奇的array.reduce方法的10個案例
- array.map 幫你精通JS:神奇的array.map的6個案例
- array.flat and array.flatMap 幫你精通JS: array.flat與flatMap用法指南
為什么一種編程語言要耗費如此巨大的精力在數(shù)組這種數(shù)據(jù)結(jié)構(gòu)上呢?因為數(shù)組是我們?nèi)粘K伎嫉幕?。比如你早起籌劃的一天的任務(wù)是數(shù)組,你的購物清單是數(shù)組。
學(xué)習(xí)編程,絕不能將紙上的字符簡簡單單的只當(dāng)作抽象的無意義的符號,我們要還原為,具象為具體的生活與應(yīng)用之中。
接下來,我們繼續(xù)構(gòu)建我們的思維。介紹用于邏輯判斷的array.filter, array.find, array.findIndex以及array.include(array.find應(yīng)用于具體個例)和array.indexOf(array.findInde用于具體個例) 和array.some, array.every.
1.1 array.filter() 概述
filter() 方法創(chuàng)建一個新數(shù)組, 其包含通過所提供函數(shù)實現(xiàn)的測試的所有元素。
語法
- var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
參數(shù)
callback 用來測試數(shù)組的每個元素的函數(shù)。返回 true 表示該元素通過測試,保留該元素,false 則不保留。它接受以下三個參數(shù):
- element 數(shù)組中當(dāng)前正在處理的元素。
- index可選 正在處理的元素在數(shù)組中的索引。
- array可選 調(diào)用了 filter 的數(shù)組本身。
thisArg可選 執(zhí)行 callback 時,用于 this 的值。
返回值
一個新的、由通過測試的元素組成的數(shù)組,如果沒有任何數(shù)組元素通過測試,則返回空數(shù)組。
1.2 array.filter() 描述
filter 為數(shù)組中的每個元素調(diào)用一次 callback 函數(shù),并利用所有使得 callback 返回 true 或等價于 true 的值的元素創(chuàng)建一個新數(shù)組。callback 只會在已經(jīng)賦值的索引上被調(diào)用,對于那些已經(jīng)被刪除或者從未被賦值的索引不會被調(diào)用。那些沒有通過 callback 測試的元素會被跳過,不會被包含在新數(shù)組中。
callback 被調(diào)用時傳入三個參數(shù):
- 元素的值
- 元素的索引
- 被遍歷的數(shù)組本身
如果為 filter 提供一個 thisArg 參數(shù),則它會被作為 callback 被調(diào)用時的 this 值。否則,callback 的 this 值在非嚴(yán)格模式下將是全局對象,嚴(yán)格模式下為 undefined。callback 函數(shù)最終觀察到的 this 值是根據(jù)通常函數(shù)所看到的 "this"的規(guī)則確定的。
filter 不會改變原數(shù)組,它返回過濾后的新數(shù)組。
filter 遍歷的元素范圍在第一次調(diào)用 callback 之前就已經(jīng)確定了。在調(diào)用 filter 之后被添加到數(shù)組中的元素不會被 filter 遍歷到。如果已經(jīng)存在的元素被改變了,則他們傳入 callback 的值是 filter 遍歷到它們那一刻的值。被刪除或從來未被賦值的元素不會被遍歷到。
案例 01 篩選排除所有較小的值
下例使用 filter 創(chuàng)建了一個新數(shù)組,該數(shù)組的元素由原數(shù)組中值大于 10 的元素組成。
- function isBigEnough(element) {
- return element >= 10;
- }
- var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
- // filtered is [12, 130, 44]
案例 02 過濾 JSON 中的無效條目
以下示例使用 filter() 創(chuàng)建具有非零 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 在數(shù)組中搜索
下例使用 filter() 根據(jù)搜索條件來過濾數(shù)組內(nèi)容。
- var fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
- /**
- * Array filters items based on search criteria (query)
- */
- 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 實現(xiàn)
- const fruits = ['apple', 'banana', 'grapes', 'mango', 'orange'];
- /**
- * Array filters items based on search criteria (query)
- */
- 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() 方法返回數(shù)組中滿足提供的測試函數(shù)的第一個元素的值。否則返回 undefined。
- const array1 = [5, 12, 8, 130, 44];
- const found = array1.find(element => element > 10);
- console.log(found);
- // expected output: 12
另請參見 3.1 findIndex() 方法,它返回數(shù)組中找到的元素的索引,而不是其值。
如果你需要找到一個元素的位置或者一個元素是否存在于數(shù)組中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。
語法
- arr.find(callback[, thisArg])
參數(shù)
callback 在數(shù)組每一項上執(zhí)行的函數(shù),接收 3 個參數(shù):
- element 當(dāng)前遍歷到的元素。
- index可選 當(dāng)前遍歷到的索引。
- array可選 數(shù)組本身。
thisArg可選 執(zhí)行回調(diào)時用作this 的對象。
返回值
數(shù)組中第一個滿足所提供測試函數(shù)的元素的值,否則返回 undefined。
2.2 array.find() 描述
find方法對數(shù)組中的每一項元素執(zhí)行一次 callback 函數(shù),直至有一個 callback 返回 true。當(dāng)找到了這樣一個元素后,該方法會立即返回這個元素的值,否則返回 undefined。注意 callback 函數(shù)會為數(shù)組中的每個索引調(diào)用即從 0 到 length - 1,而不僅僅是那些被賦值的索引,這意味著對于稀疏數(shù)組來說,該方法的效率要低于那些只遍歷有值的索引的方法。
callback函數(shù)帶有3個參數(shù):當(dāng)前元素的值、當(dāng)前元素的索引,以及數(shù)組本身。
如果提供了 thisArg參數(shù),那么它將作為每次 callback函數(shù)執(zhí)行時的this ,如果未提供,則使用 undefined。
find方法不會改變數(shù)組。
在第一次調(diào)用 callback函數(shù)時會確定元素的索引范圍,因此在 find方法開始執(zhí)行之后添加到數(shù)組的新元素將不會被 callback函數(shù)訪問到。如果數(shù)組中一個尚未被callback函數(shù)訪問到的元素的值被callback函數(shù)所改變,那么當(dāng)callback函數(shù)訪問到它時,它的值是將是根據(jù)它在數(shù)組中的索引所訪問到的當(dāng)前值。被刪除的元素仍舊會被訪問到,但是其值已經(jīng)是undefined了。
案例 01 用對象的屬性查找數(shù)組里的對象
- 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 尋找數(shù)組中的質(zhì)數(shù)
下面的例子展示了如何從一個數(shù)組中尋找質(zhì)數(shù)(如果找不到質(zhì)數(shù)則返回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
當(dāng)在回調(diào)中刪除數(shù)組中的一個值時,當(dāng)訪問到這個位置時,其傳入的值是 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]設(shè)置為undefined,可以試試用a.pop()刪除最后一項,依然會遍歷到被刪的那一項
- }
- // Element 5 is still visited even though deleted
- console.log('Visited index ' + index + ' with value ' + value);
- });
3.1 array.findIndex() 概述
findIndex()方法返回數(shù)組中滿足提供的測試函數(shù)的第一個元素的索引。若沒有找到對應(yīng)元素則返回-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() 方法,它返回數(shù)組中找到的元素的值,而不是其索引。
語法
- arr.findIndex(callback[, thisArg])
參數(shù)
callback 針對數(shù)組中的每個元素, 都會執(zhí)行該回調(diào)函數(shù), 執(zhí)行時會自動傳入下面三個參數(shù):
- element 當(dāng)前元素。
- index 當(dāng)前元素的索引。
- array 調(diào)用findIndex的數(shù)組。
thisArg 可選。執(zhí)行callback時作為this對象的值.
返回值
數(shù)組中通過提供測試函數(shù)的第一個元素的索引。否則,返回-1
3.2 array.findIndex() 描述
findIndex方法對數(shù)組中的每個數(shù)組索引0..length-1(包括)執(zhí)行一次callback函數(shù),直到找到一個callback函數(shù)返回真實值(強制為true)的值。如果找到這樣的元素,findIndex會立即返回該元素的索引。如果回調(diào)從不返回真值,或者數(shù)組的length為0,則findIndex返回-1。 與某些其他數(shù)組方法(如Array#some)不同,在稀疏數(shù)組中,即使對于數(shù)組中不存在的條目的索引也會調(diào)用回調(diào)函數(shù)。
回調(diào)函數(shù)調(diào)用時有三個參數(shù):元素的值,元素的索引,以及被遍歷的數(shù)組。
如果一個 thisArg 參數(shù)被提供給 findIndex, 它將會被當(dāng)作this使用在每次回調(diào)函數(shù)被調(diào)用的時候。如果沒有被提供,將會使用undefined。
findIndex不會修改所調(diào)用的數(shù)組。
在第一次調(diào)用callback函數(shù)時會確定元素的索引范圍,因此在findIndex方法開始執(zhí)行之后添加到數(shù)組的新元素將不會被callback函數(shù)訪問到。如果數(shù)組中一個尚未被callback函數(shù)訪問到的元素的值被callback函數(shù)所改變,那么當(dāng)callback函數(shù)訪問到它時,它的值是將是根據(jù)它在數(shù)組中的索引所訪問到的當(dāng)前值。被刪除的元素仍然會被訪問到。
案例 01 查找數(shù)組中首個質(zhì)數(shù)元素的索引
以下示例查找數(shù)組中素數(shù)的元素的索引(如果不存在素數(shù),則返回-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() 概述
當(dāng)2.1的array.find查找具體的元素的時候,就是array.include。
includes() 方法用來判斷一個數(shù)組是否包含一個指定的值,根據(jù)情況,如果包含則返回 true,否則返回false。
語法
- arr.includes(valueToFind[, fromIndex])
參數(shù)
valueToFind
需要查找的元素值。
Note: 使用 includes()比較字符串和字符時是區(qū)分大小寫。
fromIndex 可選 從fromIndex 索引處開始查找 valueToFind。如果為負(fù)值,則按升序從 array.length + fromIndex 的索引開始搜 (即使從末尾開始往前跳 fromIndex 的絕對值個索引,然后往后搜尋)。默認(rèn)為 0。
返回值
返回一個布爾值 Boolean ,如果在數(shù)組中找到了(如果傳入了 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 大于等于數(shù)組長度
如果 fromIndex 大于等于數(shù)組的長度,則會返回 false,且該數(shù)組不會被搜索。
- var arr = ['a', 'b', 'c'];
- arr.includes('c', 3); // false
- arr.includes('c', 100); // false
案例 02 計算出的索引小于 0
如果 fromIndex 為負(fù)值,計算出的索引將作為開始搜索searchElement的位置。如果計算出的索引小于 0,則整個數(shù)組都會被搜索。
- // 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() 方法有意設(shè)計為通用方法。它不要求this值是數(shù)組對象,所以它可以被用于其他類型的對象 (比如類數(shù)組對象)。下面的例子展示了 在函數(shù)的 arguments 對象上調(diào)用的 includes() 方法。
- (function() {
- console.log([].includes.call(arguments, 'a')); // true
- console.log([].includes.call(arguments, 'd')); // false
- })('a','b','c');
5.1 array.indexOf() 概述
indexOf()方法返回在數(shù)組中可以找到一個給定元素的第一個索引,如果不存在,則返回-1。
語法
- arr.indexOf(searchElement[, fromIndex])
參數(shù)
- searchElement
要查找的元素
- fromIndex 可選
開始查找的位置。如果該索引值大于或等于數(shù)組長度,意味著不會在數(shù)組里查找,返回-1。如果參數(shù)中提供的索引值是一個負(fù)值,則將其作為數(shù)組末尾的一個抵消,即-1表示從最后一個元素開始查找,-2表示從倒數(shù)第二個元素開始查找 ,以此類推。 注意:如果參數(shù)中提供的索引值是一個負(fù)值,并不改變其查找順序,查找順序仍然是從前向后查詢數(shù)組。如果抵消后的索引值仍小于0,則整個數(shù)組都將會被查詢。其默認(rèn)值為0.
返回值
首個被找到的元素在數(shù)組中的索引位置; 若沒有找到則返回 -1
5.2 array.indexOf() 描述
indexOf 使用strict equality (無論是 ===, 還是 triple-equals操作符都基于同樣的方法)進(jìn)行判斷 searchElement與數(shù)組中包含的元素之間的關(guān)系。
案例 01 使用indexOf
以下例子使用indexOf方法確定多個值在數(shù)組中的位置。
- 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 找出指定元素出現(xiàn)的所有位置
- 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 判斷一個元素是否在數(shù)組里,不在則更新數(shù)組
- 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.總結(jié)
以上介紹了array用作高階邏輯判斷的5個函數(shù):
array.filter, array.find, array,findIndex, array.includes, array.indexOf
另外還有比較常用但是較為簡單的array.every與array.some也用作邏輯判斷,此處略去不提。