摘要:如果要這樣做,就需要將選擇的過濾條件先記錄下來。現在再用最后取得的依次來過濾原數據,就能得到正確的結果。不過這樣我覺得效率不太好,尤其是對大量數據的時候。
問題提出所有代碼使用 ES2015 語法,需要 ES5 語法的可以用 Babel - Try it out 或者 TypeScript Playground 翻譯。
今天有朋友問我一個問題,前端通過 Ajax 從后端取得了大量的數據,需要根據一些條件過濾,過濾的方法是這樣的:
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); } }
現在迷糊了,覺得這樣處理數據不對,但是又不知道該怎么處理。
發現問題問題就在過濾上,這樣固然可以實現多重過濾(先調用 filterA() 再調用 filterB() 就可以實現),但是這個過濾是不可逆的。假如過濾過程是這樣:
f.filterA("a1"); f.filterB("b1"); f.filterA("a2");
本來是希望按 "a1" 和 "b1" 過濾了數據之后,再修改第一個條件為 "a2",但結果卻成了空集。
解決問題發現了問題,就針對性的解決。這個問題既然是因為過濾過程不可逆造成的,那每次都直接從 this.data 開始過濾,而不是從 this.filterData 開始過濾,就能解決問題。如果要這樣做,就需要將選擇的過濾條件先記錄下來。
記錄過濾條件用一個列表記錄過濾條件當然是可行的,但是注意對同一個條件的兩次過濾是互斥的,只能保留最后一個,所以應該用 HashMap 更為合適。
class Filter { constructor() { this.filters = {}; } set(key, filter) { this.filters[key] = filter; } getFilters() { return Object.keys(this.filters).map(key => this.filters[key]); } }
這種情況下,像上面的過程表示為
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 句設置的 filter 覆蓋了第 1 句設置的那個。現在再用最后取得的 filters 依次來過濾原數據 this.data,就能得到正確的結果。
有人會覺得 getFilters() 返回的列表不是按 set 的順序的——的確,這是 HashMap 的特點,無序。不過對于簡單條件的判斷,不管誰先誰后,結果是一樣的。但是對于一些復合條件判斷,就可能會有影響。
確實需要的話,可以通過 array 代替 map 來解決一下順序的問題,但這樣查找效率會降低(線性查找)。如果還想解決查找效率的問題,可以用 array + map 來處理。這里就不多說了。
過濾實際上在使用的時候,每次都 getFilter() 再用一個循環來處理確實比較慢。既然 data 都封裝成 Filter 中,可以考慮直接給一個 filter() 方法來作為過濾接口。
class Filter { filter() { let data = this.data; for (let f of this.getFilters()) { data = data.filter(f); } return data; } }
不過這樣我覺得效率不太好,尤其是對大量數據的時候。不妨利用一下 lodash 的延遲處理過程。
利用 lodash 的延遲處理filter() { let chain = _(this.data); for (let f of this.getFilters()) { chain = chain.filter(f); } return chain.value(); }
lodash 在數據大于 200 的時候會啟用延遲處理過程,也就是說,它會處理成一個循環中依次調用每一個 filter,而不是對每一個 filter 進行一次循環。
延遲處理和非延遲處理通過下圖可以看出來區別。非延遲處理總共會進行 n(這里 n = 3) 次大循環,產生 n - 1 個中間結果。而延遲處理只會進行一次大循環,沒有中間結果產生。
不過說實在的,我不太喜歡為了一點小事多加載一個庫,所以干脆自己做個簡單的實現
自己實現延遲處理filter() { const filters = this.getFilters(); return data.filter(m => { for (let f of filters) { // 如果某個 filter 已經把它過濾掉了,也不用再用后面的 filter 來判斷了 if (!f(m)) { return false; } } return true; }); }
里面的 for 循環還可以用 Array.prototype.every 來簡化:
filter() { const filters = this.getFilters(); return data.filter(m => { return filters.every(f => f(m)); }); }
數據過濾其實并不是多復雜的事情,只要把思路理清楚,搞明白什么數據是需要保留的,什么數據是臨時(中間過程)的,什么數據是最終結果……利用 Array.prototype 中的相關方法,或者諸如 lodash 之類的工具,很容易就處理出來了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/80752.html
摘要:首發地址識別認證與安全第三部分的章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。安全使用基本認證的唯一方式就是將其與配合使用。加密之前的原始報文通常被稱為明文或。 WilsonLius blog 首發地址 識別,認證與安全 第三部分的4章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。 客戶端識別與cookie機制 第十一章 H...
摘要:首發地址識別認證與安全第三部分的章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。安全使用基本認證的唯一方式就是將其與配合使用。加密之前的原始報文通常被稱為明文或。 WilsonLius blog 首發地址 識別,認證與安全 第三部分的4章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。 客戶端識別與cookie機制 第十一章 H...
摘要:首發地址識別認證與安全第三部分的章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。安全使用基本認證的唯一方式就是將其與配合使用。加密之前的原始報文通常被稱為明文或。 WilsonLius blog 首發地址 識別,認證與安全 第三部分的4章提供了一系列的技術和機器,可用來跟蹤身份,進行安全性檢測,控制對內容的訪問。 客戶端識別與cookie機制 第十一章 H...
摘要:比特幣白皮書都發布了年,這一種資產保護模式一直延續到現在。如果考慮到一個人除了比特幣以外如果還擁有其他數字資產,一個資產一個私鑰,那么私鑰的管理難度就更大了。以用戶在網絡存儲比特幣為例。用戶存入的比特幣只有經過節點批準才能提走。 **Mixin開發者大賽正在進行中,javascript, python, ruby, go程序員都可以來贏30萬大獎。 大賽地址活動地址 mixin.one...
閱讀 2049·2021-10-08 10:05
閱讀 1889·2021-09-22 15:31
閱讀 3012·2021-09-22 15:13
閱讀 3488·2021-09-09 09:34
閱讀 2091·2021-09-03 10:46
閱讀 3125·2019-08-30 15:56
閱讀 1705·2019-08-30 15:53
閱讀 2360·2019-08-30 15:44