在上家公司工作的時候,有一個需求是在前端部分完成篩選功能,一次拿到所有資料,然後根據條件篩選。通常情況下篩選是後臺給介面,在資料量不大的情況下,也有人可能會遇到前端篩選這樣的情況,特別寫了這篇文章分享給大家,有問題請指出,互相學習。
一般情況下的單條件篩選,陣列的filter方法就能夠滿足需求,本文討論的重點是多條件下的複合篩選,並列出了幾個相關知識點。
以下是很多個????
// 這個是例子中的被篩選陣列
var aim = [
{name:'Anne', age: 23, gender:'female'},
{name:'Leila', age: 16, gender:'female'},
{name:'Jay', age: 19, gender:'male'},
{name:'Mark', age: 40, gender:'male'}
]
複製程式碼
單條件單資料篩選
根據單個名字篩選,用filter方法,判斷name是否為目標名字即可
// 根據單個名字篩選
function filterByName(aim, name) {
return aim.filter(item => item.name == name)
}
// 輸入 aim 'Leila' 期望輸出為 [{name:'Leila', age: 16, gender:'female'}]
console.log(filterByName(aim,'leila'))
複製程式碼
單條件多資料篩選
根據多個名字篩選,這裡是用for迴圈遍歷目標陣列,然後用find方法找到後push到結果陣列裡,用find方法是重名情況下也能得到想要的結果。for迴圈可以用陣列的一些遍歷方法替代,程式碼可以更簡化,示例就是大概表達個意思。
// 根據多個名字篩選
function filterByName1(aim, nameArr) {
let result = []
for(let i = 0; i < nameArr.length; i++) {
result.push(aim.find(item => item.name = nameArr[i]))
}
return result
}
// 輸入 aim ['Anne','Jay']
//期望輸出為 [{name:'Anne', age: 23, gender:'female'},{name:'Jay', age: 19, gender:'male'}]
console.log(filterByName1(aim,['Leila','Jay']))
// 有BUG 改進後
複製程式碼
多條件單資料篩選
根據單個名字或者單個年齡篩選,用filter方法,判斷條件之間是或的關係。
// 根據名字或者年齡篩選
function filterByName2(aim, name, age) {
return aim.filter(item => item.name == name || item.age == age)
}
console.log(filterByName2(aim,'Leila',19))
複製程式碼
多條件多資料篩選
我最初是用了很笨的雙for迴圈去做,發現很慢,而且並沒有達到預期的效果。具體的心路歷程已經太遙遠,簡單介紹以下這個篩選演算法。
首先是把篩選條件都塞到一個物件裡,用object物件的keys方法獲取到篩選的條件名,及需要篩選的是哪個條件,是name?age? gender?
然後使用filter方法對目標資料進行篩選,?如下⬇️
根據名字和年齡多元素篩選
//根據名字和年齡多元素篩選
export function multiFilter(array, filters) {
const filterKeys = Object.keys(filters)
// filters all elements passing the criteria
return array.filter((item) => {
// dynamically validate all filter criteria
return filterKeys.every(key => {
//ignore when the filter is empty Anne
if(!filters[key].length) return true
return !!~filters[key].indexOf(item[key])
})
})
}
/*
* 這段程式碼並非我原創,感興趣的可以去原作者那裡點個贊
* 作者是:@author https://gist.github.com/jherax
* 這段程式碼裡我只加了一行,解決部分篩選條件清空時候整體篩選失效的問題
*/
var filters = {
name:['Leila', 'Jay'],
age:[]
}
/* 結果:
* [{name: "Leila", age: 16, gender: "female"},
* {name: "Jay", age: 19, gender: "male"}]
*/
複製程式碼
例如這裡,判斷每條資料的name值是否在filters.name陣列裡,是的話返回true,判斷filters.age是空陣列的話直接返回true,空陣列是模擬了age條件被清空的情況,我們仍然能得到正確的篩選資料。
知識點1: Object.key() 獲取陣列索引或者物件屬性
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr));
// ["0", "1", "2"]
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj));
// ["0", "1", "2"]
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj));
// ["2", "7", "100"] 猜猜為啥?
複製程式碼
知識點2: js裡的falsy
falsy : 0 , false, "", null, undefined, NaN
在判斷語句中,只有上面6種情況會是false,其餘的都是true
var a;
if(a!=null&&typeof(a)!=undefined&&a!=''){
//a有內容才執行的程式碼
}
if(!!a){
//a有內容才執行的程式碼...
}
複製程式碼
知識點3: Array.every 與 Array.some的區別
我的理解是在遍歷陣列的時候:
Array.every的條件是「與」的關係,全真為真,及條件全為true則為true,有一個false就返回false
Array.some的條件是「或」的關係,有真為真,條件有一個true就返回true,條件全為false的時候才返回false
下面舉個?
// 判斷每個名字都為Anne?
let dataEvery = aim.every(item => item.name === 'Anne') // false
let dataEvery = aim.some(item => item.name === 'Anne') // true
// 判斷每個名字都是字串?
let dataEvery = aim.every(item => typeof item.name === 'string') // true
let dataEvery = aim.some(item => typeof item.name === 'string') // true
複製程式碼
知識點4: 陣列的深拷貝與淺拷貝
最近參與一些前端的面試工作,深拷貝與淺拷貝是我最愛問的問題之一。一個問題就考察了資料型別,陣列操作,遞迴演算法等。
因為陣列是js裡的引用型別,單純複製時複製的是其引用關係。在對獲取的資料進行篩選時,我並不希望影響原始資料,所以我要用到「深拷貝」得到與原始資料資料結構完全相同又相互獨立的資料,而不是隻複製其引用關係。
// 我常用方法,如果專案很大,不推薦
let obj1 = JSON.parse(JSON.stringify(obj))
// deepclone
function deepClone(o1, o2) {
for (let k in o2) {
if (typeof o2[k] === 'object') {
o1[k] = {};
deepClone(o1[k], o2[k]);
} else {
o1[k] = o2[k];
}
}
}
複製程式碼
想一想:遞迴演算法的優化
這個知識點與本文關係不大。? 抱歉之前的誤導。
這個是看掘金小冊前端面試指南看到的,講演算法的時候提了一下遞迴演算法的優化,初見的時候又被驚豔到,還沒有在專案裡用到。感興趣的可以試試,這個是斐波那契數列和。可以自己在瀏覽器裡敲一下,試試不用快取與用快取的運算次數差別。
let count = 0;
function fn(n) {
let cache = {};
function _fn(n) {
if (cache[n]) {
return cache[n];
}
count++;
if (n == 1 || n == 2) {
return 1;
}
let prev = _fn(n - 1);
cache[n - 1] = prev;
let next = _fn(n - 2);
cache[n - 2] = next;
return prev + next;
}
return _fn(n);
}
let count2 = 0;
function fn2(n) {
count2++;
if (n == 1 || n == 2) {
return 1;
}
return fn2(n - 1) + fn2(n - 2);
}
複製程式碼
完!