ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript での大量のデータの複数のフィルタリング

JavaScript での大量のデータの複数のフィルタリング

高洛峰
高洛峰オリジナル
2016-11-08 09:58:031178ブラウズ

すべてのコードは 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 個の中間結果が生成されます。遅延処理は大きなループのみを実行し、中間結果は生成されません。

JavaScript での大量のデータの複数のフィルタリング

でも正直、ちょっとしたことで余計なライブラリをロードするのは嫌なので、簡単な実装だけ自分で作ります

遅延処理は自分で実装

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 などのツールを簡単に扱うことができます。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。