前書き
主な要件は、フロントエンドが Ajax を通じてバックエンドから大量のデータを取得することであり、これをいくつかの条件に従ってフィルタリングする必要があります。最初のフィルタリング方法は次のとおりです。
今私は混乱しており、このようにデータを処理するのは正しくないと感じていますが、どう対処すればよいかわかりません。
問題が見つかりました
問題はフィルタリングにあります。この方法で複数のフィルタリングを実現できますが (これは、最初に filterA() を呼び出し、次に filterB() を呼び出すことで実現できます)、このフィルタリングは元に戻すことができません。
フィルタリングプロセスが次のようになっているとします。
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); } }
当初、「a1」と「b1」でデータをフィルタリングし、最初の条件を「a2」に変更したかったのですが、結果は次のようになりました。空のセット。
問題を解決する
問題を見つけたら、それに応じて解決してください。この問題は不可逆的なフィルタリング プロセスによって発生するため、this.filterData から開始するのではなく、毎回 this.data から直接フィルタリングを開始することで問題を解決できます。これを行う場合は、まず選択したフィルター条件を記録する必要があります。
フィルター条件を記録する
f.filterA("a1"); f.filterB("b1"); f.filterA("a2");
この場合、上記のような処理は
class Filter { constructor() { this.filters = {}; } set(key, filter) { this.filters[key] = filter; } getFilters() { return Object.keys(this.filters).map(key => this.filters[key]); } }
と表現されます。
上記の 3 番目の文で設定されたフィルタは、1 番目の文で設定されたフィルタをカバーします。最後に取得したフィルターを使用して、元のデータ this.data を順番にフィルター処理すると、正しい結果が得られます。
getFilters() によって返されるリストは set の順序ではないと考える人もいるかもしれません。実際、これは順序のない HashMap の特性です。ただし、単純な条件の判定ではどちらが先でも結果は同じです。ただし、一部の複合条件の判断では、影響がある可能性があります。
必要に応じて、マップの代わりに配列を使用して順序の問題を解決できますが、これにより検索効率(線形検索)が低下します。それでも検索効率の問題を解決したい場合は、配列 + マップを使用できます。ここで言うことはあまりありません。
フィルタリング
実際に使ってみると、毎回getFilter()を使ってループを使って処理しているととても遅いです。データは Filter にカプセル化されるため、filter() メソッドを直接指定してフィルタリング インターフェイスを提供することを検討できます。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;
しかし、特に大量のデータを扱う場合、これはあまり効率的ではないと思います。 lodash の遅延処理を利用するのもよいでしょう。
lodash の遅延処理を使用すると
class Filter { filter() { let data = this.data; for (let f of this.getFilters()) { data = data.filter(f); } return data; } }
lodash は、データが 200 を超える場合に遅延処理を有効にします。つまり、データをループに処理し、各フィルターを呼び出すのではなく、各フィルターを順番に呼び出します。ループ。
遅延処理と遅延しない処理の違いは、以下の図で確認できます。非遅延処理では、合計 n (ここでは n = 3) 個の大きなループが実行され、n - 1 個の中間結果が生成されます。遅延処理は大きなループのみを実行し、中間結果は生成されません。
でも正直、ちょっとしたことで余計なライブラリをロードするのは嫌なので、簡単な実装だけ自分で作ってます遅延処理を自分で実装
filter() { let chain = _(this.data); for (let f of this.getFilters()) { chain = chain.filter(f); } return chain.value(); }
中のforループ
filter() { const filters = this.getFilters(); return data.filter(m => { for (let f of filters) { // 如果某个 filter 已经把它过滤掉了,也不用再用后面的 filter 来判断了 if (!f(m)) { return false; } } return true; }); }
Array.prototype.every を使用して簡略化することもできます。アイデアを明確にし、どのデータを保持する必要があるか、どのデータが一時的なデータであるかを理解している限り、実際には複雑な問題ではありません。 process)、最終結果はどのようなデータになるのか... Array.prototype の関連メソッドや lodash などのツールを使用して簡単に処理できます。