JavaScript 陣列處理:找東西 - indexOf、$.inArray 與 filter
08 May 2017整理了一些在陣列中找東西的方法。
尋找是否有符合的元素
方法一:indexOf
使用原生 JavaScript 的Array.prototype.indexOf()
。
如下例,陣列 fruit 中有三種水果,分別是蘋果 (apple)、橘子 (orange) 和 葡萄 (grape)。
找「芒果」(mango),回傳 -1,表示找不到。
找「葡萄」(grape),回傳 2,表示在陣列中的第三個位置找到。
var fruits = ['apple', 'orange', 'grape'];
fruits.indexOf('mango'); // -1
fruits.indexOf('grape'); // 2
方法二:$.inArray
使用 jQuery 的 $.inArray
。
如下例,在陣列 fruit 中找水果。
找「鳳梨」(pineapple),回傳 -1,表示找不到。
找「葡萄」(grape),回傳 2,表示在陣列中的第三個位置找到。
var fruits = ['apple', 'orange', 'grape'];
$.inArray('pineapple', fruits); // -1
$.inArray('grape', fruits); // 2
方法三:filter
使用 Array.prototype.filter()
回傳符合條件的元素,得到一個新陣列。
如下例,回傳大於 10 的數字。
var numbers = [1, 3, 6, 10, 99, 101];
var filteredNum = numbers.filter(function(value) {
return value > 10;
});
filteredNum; // [99, 101]
找「鳳梨」(pineapple),回傳空陣列,表示找不到。
var fruits = ['apple', 'orange', 'grape'];
var filtered = fruits.filter(function(value) {
return value === 'pineapple';
});
filtered; // []
找「葡萄」(grape),回傳含有 grape 字串的陣列,表示找到。
filtered; // ["grape"]
又,在一陣列包含陣列的複雜結構中,要怎麼找到符合條件的元素呢?如下,找到開頭為「小小」的字串。(2020/02/25 更新)
const list = [
['小明', '小小明', '小明明'],
['小華', '小小華', '小華華'],
['小美', '小小美', '小美美'],
];
const newList = [].concat(...list); // newList = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = newList.filter((value) => value.match(/^小小/g));
console.log(result); // ["小小明", "小小華", "小小美"]
解法為先打平陣列再做搜尋,可參考這裡。
點此看 demo。
再複雜一點,一陣列包含許多物件,然後我們想找某物件是否在這個陣列裡面,並且這個物件的某個欄位是否合乎預期。
如下,這裡有兩個物件 targetStack 和 anotherTargetStack,它們都包含一些屬性以供辨識。
const targetStack = {
"awsRegion": "us-west-2",
"vpcId": "vpc-1234567",
"awsAccountId": "123456789",
"stackName": "hello-world-12345",
"stackStatus": "CREATE_COMPLETE",
};
const anotherTargetStack = {
"awsRegion": "us-west-2",
"vpcId": "vpc-1234567",
"awsAccountId": "987654321",
"stackName": "hello-world-12345",
"stackStatus": "CREATE_COMPLETE",
};
目標是看看能不能在這個 stacks 裡面找到 targetStack 或 anotherTargetStack。
const stacks = [
{
"awsRegion": "us-west-2",
"vpcId": "vpc-1234567",
"awsAccountId": "123456789",
"stackName": "C1NSv2-10-478358581025-vpc-0ff713091ef7a2d83",
"stackStatus": "CREATE_COMPLETE",
"serviceName": "hello-world-12345",
"metadata": {}
}
];
實作 findDeployedStack 來做找尋的工作,其中…
- 以 awsAccountId 當成可辨識的欄位。
- 除了找到這個 stack 之外,還要確定它的某個狀態是否合乎預期,例如:希望它的狀態是
CREATE_COMPLETE
。
const findDeployedStack = ({ targetStack, stacks }) => {
const findResult = stacks.filter((item) => {
return item.awsAccountId === targetStack.awsAccountId;
})[0];
return findResult?.stackStatus === 'CREATE_COMPLETE';
};
結果如下,有找到 targetStack,但沒有找到 anotherTargetStack。
const found = findDeployedStack({ targetStack, stacks });
console.log(`found: ${found}`); // "found: true"
const foundAnotherOne = findDeployedStack({ targetStack: anotherTargetStack, stacks });
console.log(`foundAnotherOne: ${foundAnotherOne}`); // "foundAnotherOne: false"
一次找一個目標太麻煩了,把所有要找的目標當成 array 傳進去。
在下面這段程式碼中,先用 map 將要找的目標物件做迭代,然後用 find 來個別比對目標物件與陣列裡面的元素們是否有欄位相符的,最後再將這些欄位比對相符的用 filter 檢視它們的狀態是否如同要求。注意,用 find 就可以省去結構調整的問題,因為 find 回傳元素後與 map 拿到的陣列即可提供 filter 做篩選。
const findDeployedStackList = ({ targetStacks = [], stacks }) => {
return targetStacks
.map((targetStack) => stacks.find(item => item.awsAccountId === targetStack.awsAccountId))
.filter((findResult) => findResult?.stackStatus === 'CREATE_COMPLETE');
};
const foundResult = findDeployedStackList({ targetStacks: [targetStack, anotherTargetStack], stacks });
console.log(`foundResult: `, foundResult); // 得到符合條件的 stack
結果如下,得到符合條件的 stack。
"foundResult: "
[
{
"awsRegion": "us-west-2",
"vpcId": "vpc-1234567",
"awsAccountId": "123456789",
"stackName": "C1NSv2-10-478358581025-vpc-0ff713091ef7a2d83",
"stackStatus": "CREATE_COMPLETE",
"serviceName": "hello-world-12345",
"metadata": {}
}]
點此看 demo。
方法四:includes
ES2016 加入了 Array.prototype.includes()
,可在陣列中尋找指定元素,回傳 true(找到) 或 false(沒找到)。注意,IE 不支援。
var fruits = ['apple', 'orange', 'banana'];
console.log(fruits.includes('orange')); // true
console.log(fruits.includes('pineapple')); // false
參考 Array.prototype.includes() - JavaScript - MDN(2018/02/18 更新)
尋找是否有符合的物件:indexOf
與 $.map
由於無法直接在陣列中比對物件,因此必須先簡化這個含有許多物件的陣列,成為只含有單一欄位的陣列,再做尋找比對。其中欄位的內容可為字串、數字這種基礎型別的值。
如下例,想要在陣列 people 中尋找 Nina。陣列 people 含有許多物件 person,每個 person 含有 name 和 age 兩個欄位。由於目標「Nina」是 name,因此先把 people 簡化為只含 name 的新陣列。在這裡使用$.map
回傳只含有特定欄位 name 的新陣列。
再來,使用上面提到的indexOf
在新陣列中尋找是否有 Nina 這個字串。回傳 2 表示在陣列第三個元素中找到。
var people = [
{ name: 'Peter', age: 10 },
{ name: 'John', age: 3 },
{ name: 'Nina', age: 15 },
];
//find object in list
var result = $.map(people, function(item, index) {
return item.name;
}).indexOf('Nina');
result; // 2
indexOf
vs $.inArray
以下是 jQuery 的原始碼。若瀏覽器支援,則預設是使用原生 JavaScript 的indexOf
。
inArray: function( elem, array, i ) {
var len;
if ( array ) {
if ( indexOf ) {
return indexOf.call( array, elem, i );
}
len = array.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in array && array[ i ] === elem ) {
return i;
}
}
}
return -1;
}