JS對(duì)大量數(shù)據(jù)進(jìn)行多重過(guò)濾的方法
前言
主要的需求是前端通過(guò) Ajax 從后端取得了大量的數(shù)據(jù),需要根據(jù)一些條件過(guò)濾,首先過(guò)濾的方法是這樣的:
class Filter { filterA(s) { let data = this.filterData || this.data; this.filterData = data.filter(m => m.a === s); } filterB(s) { let data = this.filterData || this.data; this.filterData = data.filter(m => m.b === s); } }
現(xiàn)在迷糊了,覺(jué)得這樣處理數(shù)據(jù)不對(duì),但是又不知道該怎么處理。
發(fā)現(xiàn)問(wèn)題
問(wèn)題就在過(guò)濾上,這樣固然可以實(shí)現(xiàn)多重過(guò)濾(先調(diào)用 filterA()
再調(diào)用 filterB()
就可以實(shí)現(xiàn)),但是這個(gè)過(guò)濾是不可逆的。
假如過(guò)濾過(guò)程是這樣:
f.filterA("a1"); f.filterB("b1"); f.filterA("a2");
本來(lái)是希望按 "a1" 和 "b1" 過(guò)濾了數(shù)據(jù)之后,再修改第一個(gè)條件為 "a2",但結(jié)果卻成了空集。
解決問(wèn)題
發(fā)現(xiàn)了問(wèn)題,就針對(duì)性的解決。這個(gè)問(wèn)題既然是因?yàn)檫^(guò)濾過(guò)程不可逆造成的,那每次都直接從 this.data
開(kāi)始過(guò)濾,而不是從 this.filterData
開(kāi)始過(guò)濾,就能解決問(wèn)題。如果要這樣做,就需要將選擇的過(guò)濾條件先記錄下來(lái)。
記錄過(guò)濾條件
用一個(gè)列表記錄過(guò)濾條件當(dāng)然是可行的,但是注意對(duì)同一個(gè)條件的兩次過(guò)濾是互斥的,只能保留最后一個(gè),所以應(yīng)該用 HashMap 更為合適。
class Filter { constructor() { this.filters = {}; } set(key, filter) { this.filters[key] = filter; } getFilters() { return Object.keys(this.filters).map(key => this.filters[key]); } }
這種情況下,像上面的過(guò)程表示為
f.set("A", m => m.a === "a1"); f.set("B", m => m.b === "b1"); f.set("A", m => m.a === "a1"); let filters = f.getFilters(); // length === 2;
上面第 3 句設(shè)置的 filter 覆蓋了第 1 句設(shè)置的那個(gè)?,F(xiàn)在再用最后取得的 filters 依次來(lái)過(guò)濾原數(shù)據(jù) this.data
,就能得到正確的結(jié)果。
有人會(huì)覺(jué)得 getFilters()
返回的列表不是按 set 的順序的——的確,這是 HashMap 的特點(diǎn),無(wú)序。不過(guò)對(duì)于簡(jiǎn)單條件的判斷,不管誰(shuí)先誰(shuí)后,結(jié)果是一樣的。但是對(duì)于一些復(fù)合條件判斷,就可能會(huì)有影響。
確實(shí)需要的話,可以通過(guò) array 代替 map 來(lái)解決一下順序的問(wèn)題,但這樣查找效率會(huì)降低(線性查找)。如果還想解決查找效率的問(wèn)題,可以用 array + map 來(lái)處理。這里就不多說(shuō)了。
過(guò)濾
實(shí)際上在使用的時(shí)候,每次都 getFilter()
再用一個(gè)循環(huán)來(lái)處理確實(shí)比較慢。既然 data 都封裝成 Filter 中,可以考慮直接給一個(gè) filter()
方法來(lái)送貨過(guò)濾接口。
class Filter { filter() { let data = this.data; for (let f of this.getFilters()) { data = data.filter(f); } return data; } }
不過(guò)這樣我覺(jué)得效率不太好,尤其是對(duì)大量數(shù)據(jù)的時(shí)候。不妨利用一下 lodash 的延遲處理過(guò)程。
利用 lodash 的延遲處理
filter() { let chain = _(this.data); for (let f of this.getFilters()) { chain = chain.filter(f); } return chain.value(); }
lodash 在數(shù)據(jù)大于 200 的時(shí)候會(huì)啟用延遲處理過(guò)程,也就是說(shuō),它會(huì)處理成一個(gè)循環(huán)中依次調(diào)用每一個(gè) filter,而不是對(duì)每一個(gè) filter 進(jìn)行一次循環(huán)。
延遲處理和非延遲處理通過(guò)下圖可以看出來(lái)區(qū)別。非延遲處理總共會(huì)進(jìn)行 n(這里 n = 3) 次大循環(huán),產(chǎn)生 n - 1 個(gè)中間結(jié)果。而延遲處理只會(huì)進(jìn)行一次大循環(huán),沒(méi)有中間結(jié)果產(chǎn)生。
不過(guò)說(shuō)實(shí)在的,我不太喜歡為了一點(diǎn)小事多加載一個(gè)庫(kù),所以干脆自己做個(gè)簡(jiǎn)單的實(shí)現(xiàn)
自己實(shí)現(xiàn)延遲處理
filter() { const filters = this.getFilters(); return data.filter(m => { for (let f of filters) { // 如果某個(gè) filter 已經(jīng)把它過(guò)濾掉了,也不用再用后面的 filter 來(lái)判斷了 if (!f(m)) { return false; } } return true; }); }
里面的 for 循環(huán)還可以用 Array.prototype.every
來(lái)簡(jiǎn)化:
filter() { const filters = this.getFilters(); return data.filter(m => { return filters.every(f => f(m)); }); }
數(shù)據(jù)過(guò)濾其實(shí)并不是多復(fù)雜的事情,只要把思路理清楚,搞明白什么數(shù)據(jù)是需要保留的,什么數(shù)據(jù)是臨時(shí)(中間過(guò)程)的,什么數(shù)據(jù)是最終結(jié)果……利用 Array.prototype
中的相關(guān)方法,或者諸如 lodash 之類的工具,很容易就處理出來(lái)了。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能有所幫助,如果有疑問(wèn)大家可以留言交流。
相關(guān)文章
如何在Web頁(yè)面上直接打開(kāi)、編輯、創(chuàng)建Office文檔
如何在Web頁(yè)面上直接打開(kāi)、編輯、創(chuàng)建Office文檔...2007-03-03JavaScript數(shù)據(jù)結(jié)構(gòu)之單鏈表和循環(huán)鏈表
這篇文章主要介紹了JavaScript數(shù)據(jù)結(jié)構(gòu)之單鏈表、循環(huán)鏈表,詳細(xì)的介紹了JavaScript如何實(shí)現(xiàn)單鏈表、循環(huán)鏈表,有興趣的可以了解一下2017-11-11js神秘的電報(bào)密碼 哈弗曼編碼實(shí)現(xiàn)
這篇文章主要介紹了js神秘的電報(bào)密碼 哈弗曼編碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09探討JavaScript標(biāo)簽位置的存放與功能有無(wú)關(guān)系
在網(wǎng)頁(yè)中,我們可以將JavaScript代碼放在html文件中任何位置,但一般放在head或body標(biāo)簽里面。一般來(lái)說(shuō),<script>元素放在哪里與其的功能作用是緊密相關(guān)的,通過(guò)本文我們一起學(xué)習(xí)下2016-01-01JS組件Bootstrap實(shí)現(xiàn)下拉菜單效果代碼
這篇文章主要為大家詳細(xì)介紹了JS組件Bootstrap實(shí)現(xiàn)下拉菜單效果代碼,感興趣的小伙伴們可以參考一下2016-04-04火狐下input焦點(diǎn)無(wú)法重復(fù)獲取問(wèn)題的解決方法
input輸入框顯示的時(shí)候,需要自動(dòng)獲取焦點(diǎn),用focus可以輕松搞定,但在火狐下input無(wú)法獲取焦點(diǎn),下面與大家分享下不錯(cuò)的解決方法2014-06-06webpack4實(shí)現(xiàn)不同的導(dǎo)出類型
這篇文章主要介紹了webpack4實(shí)現(xiàn)不同的導(dǎo)出類型,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04