web-dev-qa-db-ja.com

JavaScriptでフィルター関数をチェーンする良い方法

複数のユーザー選択入力に基づいてフィルター処理する必要があるオブジェクトの大きなjson配列があります。現在私はフィルター機能を一緒にチェーンしていますが、これはこれを行うための最もパフォーマンスの高い方法ではない可能性が高いと感じています。

現在私はこれをやっています:

var filtered = data.filter(function(data) {
    return Conditional1
  })
  .filter(function(data) {
    return Conditional2
  })
  .filter(function(data) {
    return Conditional3
  }) etc...;

各反復で「データ」は少なくなる可能性がありますが(私は思う)、次のようなことをするほうがよいのではないかと思います。

var condition1 = Conditional1
var condition2 = Conditional2
var condition3 = Conditional3
etc...

var filtered = data.filter(function(data) {
  return condition1 && condition2 && condition3 && etc...
});

私は高次関数、特にフィルター関数の複数のチェーンを調べましたが、ベストプラクティス(または悪いプラクティス、または提案した2つの時間を計って比較したもの)については何も見ていません。

大規模なデータセットと多くの条件文が好まれる使用例では(どちらもかなり簡単に読めると思います)?

あるいは、私が見逃しているより高性能な方法があるかもしれません(ただし、より高次の関数を使用しています)。

10
eaton9000

フィルター関数を配列に格納し、array.reduce()を各フィルターで実行して、データに適用します。これには、フィルタリングするデータがなくなった場合でも、それらすべてを実行するという犠牲が伴います。

_const data = [...]
const filters = [f1, f2, f3, ...]
const filteredData = filters.reduce((d, f) => d.filter(f) , data)
_

もう1つの方法は、array.every()を使用することです。これは逆のアプローチをとり、データを実行し、すべてのフィルターが適用されるかどうかをチェックします。 array.every()は、1つのアイテムがfalseを返すとすぐにfalseを返します。

_const data = [...]
const filters = [f1, f2, f3, ...]
const filteredData = data.filter(v => filters.every(f => f(v)))
_

どちらも最初のサンプルと2番目のサンプルにそれぞれ似ています。唯一の違いは、フィルターや条件をハードコーディングしないことです。

16
Joseph

興味深い質問

data = new Array(111111).fill().map((a,n) => n);

const f1 = (a) => a % 2;
const f2 = (a) => a % 5;
const f3 = (a) => a > 347;
const filters = [f1, f2, f3];

// 1
t1 = performance.now();
res = data.filter(a=>a%2).filter(a=>a%5).filter(a=>a>347);
t2 = performance.now();
console.log("1) took " + (t2-t1) + " milliseconds.");
// 2
t1 = performance.now();
res = data.filter(a=>a%2 && a%5 && a>347);
t2 = performance.now();
console.log("2) took " + (t2-t1) + " milliseconds.");
// 3
t1 = performance.now();
res = filters.reduce((d, f) => d.filter(f) , data)
t2 = performance.now();
console.log("3) took " + (t2-t1) + " milliseconds.");
// 4
t1 = performance.now();
res = data.filter(v => filters.every(f => f(v)))
t2 = performance.now();
console.log("4) took " + (t2-t1) + " milliseconds.");

forループでも、たとえば2つのループの場合、1つの3000と1つの7を覚えておいてください。時間測定では3000x7> 7x3000。

5
nullqube

2つのオプションはまったく同じではありませんが、同じ結果が得られる可能性があります

var filtered = data.filter(function(data) {
    return Conditional1
  })
  .filter(function(data) {
    return Conditional2
  })
  .filter(function(data) {
    return Conditional3
  }) etc...;

このオプションは、互いに独立して状態をチェックする場合に適しています。 condition2でフィルタリングする前にcondition1でフィルタリングしたデータが必要な場合は、これを使用する必要があります。 3つの条件またはそれらの組み合わせに一致するアイテムをフィルタリングする場合は、2番目の条件を使用します。

var condition1 = Conditional1
var condition2 = Conditional2
var condition3 = Conditional3
etc...

var filtered = data.filter(function(data) {
  return condition1 && condition2 && condition3 && etc...
});
2
SrThompson

これを「forループ」の最適化問題と考えると、元のアプローチではリストが複数回繰り返されることがわかります。

2番目のアプローチでは、反復を1パスに減らします。

その後は、アイテムが合格するかどうかをすばやく判断するための最良の方法を見ているだけです。

0
theGleep