ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript での大量のデータの複数のフィルタリング
すべてのコードは 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.filterData から開始するのではなく、毎回 this.data から直接フィルタリングを開始することで問題を解決できます。これを行う場合は、まず選択したフィルター条件を記録する必要があります。 フィルター条件の記録 フィルター条件をリストに記録することは確かに可能ですが、同じ条件に対する 2 つのフィルターは相互に排他的であり、最後のフィルターのみを保持できることに注意してください。そのため、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 番目の文で設定されたフィルタは、1 番目の文で設定されたフィルタをカバーします。最後に取得したフィルターを使用して、元のデータ this.data を順番にフィルター処理すると、正しい結果が得られます。 getFilters() によって返されるリストは set の順序ではないと考える人もいるかもしれません。実際、これは順序のない HashMap の特性です。ただし、単純な条件の判定ではどちらが先でも結果は同じです。ただし、一部の複合条件の判断では、影響がある可能性があります。 必要に応じて、マップの代わりに配列を使用して順序の問題を解決できますが、これにより検索効率(線形検索)が低下します。それでも検索効率の問題を解決したい場合は、配列 + マップを使用できます。ここで言うことはあまりありません。 フィルタリング 実際に使ってみると、毎回getFilter()を使ってループを使って処理しているととても遅いです。データは 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 を超える場合に遅延処理を有効にします。つまり、各フィルターを 1 回ループするのではなく、ループ処理して各フィルターを順番に呼び出します。 遅延処理と遅延しない処理の違いは、以下の図で確認できます。遅延のない処理では、合計 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)); }); }アイデアを明確にし、どのデータを保持する必要があるか、どのデータが一時的 (中間プロセス) であるか、どのデータが最終的なデータであるかを理解している限り、データ フィルタリングは実際には複雑な問題ではありません。 result... 活用 Array.prototype の関連メソッドや lodash などのツールを簡単に扱うことができます。