2014-11-23 37 views
1

我有一堆存儲在一個對象中的過濾標準。標準不時變化,所以我不能有一個靜態過濾器(即:price > 5 && price < 19 && ...)。基於動態標準的高效陣列過濾

var criteria = { 
    price: { 
     min: 5, 
     max: 19 
    }, 
    age: { 
     max: 35 
    } 
}; 

我然後有一個循環的設置通過基於標準的陣列,以篩選和返回經過濾的數組:

var filtered = []; 
var add = true; 

for (var i=0; i < data.length; i++){ 
    add = true; 
    var item = data[i]; 

    for (var main in criteria){ 
     for (var type in criteria[main]){ 
      if (type === 'min') { 
       if (!(item[main] > criteria[main][type])) { 
        add = false; 
        break; 
       } 
      } else if (type === 'max') { 
       if (!(item[main] < criteria[main][type])) { 
        add = false; 
        break; 
       } 
      } 
     } 
    } 
    if (add) { 
     filtered.push(item); 
    } 
} 

是否有一個更有效的方式來設置的時間提前的濾波器條件句(即:item.price > 5 && item.price < 19 && item.age < 35)然後過濾數組?與我目前正在做的以及在每個數組循環中引用對象相反 - 這對所有的條件和子循環來說都是低效的。

查看我的jsbin - http://jsbin.com/celin/2/edit

+0

您是否考慮過下劃線的過濾器和鏈接? – Dinesh 2014-11-23 00:20:37

回答

2

我會用Array.prototype.filter

var filtered = data.filter(function (item) { 
    var main, critObj; 
    for (main in criteria) { 
    critObj = criteria[main]; 
    if (critObj.min && critObj.min >= item[main]) { 
     return false; 
    } 
    if (critObj.max && critObj.max <= item[main]) { 
     return false; 
    } 
    } 
    return true; 
}); 

回報false,如果它不應該被包含在您的過濾列表。在for循環中,函數只是檢查條件是否有最小值,以及是否大於數組項中的相同屬性。如果是這樣,它只是返回false爲這個元素(當然最大屬性相同)。

如果兩者都符合,函數返回true,並且我將被包含在您的過濾列表中!

編輯:現在fixed bin

+1

這會得到與OP不同的答案。 – 2014-11-23 05:29:17

+0

您確實看到我更改了測試標準之一?你確實看到,OP說標準對象是動態的?這個問題是關於使過濾更優雅,這很重要,我做了什麼...... – hereandnow78 2014-11-23 16:00:52

+0

你的方式肯定比我更優雅。我將最終與你同行,只是將'filter'改爲'$ .grep',這看起來更快(http://jsperf.com/grepvsfiltervsloop)。 – 2014-11-23 18:09:51

1

我一直在Ramda庫,並用它來做到這一點是相當簡單:

var test = R.allPredicates(R.reduce(function(tests, key) { 
    var field = criteria[key]; 
    if ('min' in field) {tests.push(R.pipe(R.prop(key), R.gt(R.__, field.min)));} 
    if ('max' in field) {tests.push(R.pipe(R.prop(key), R.lt(R.__, field.max)));} 
    return tests; 
}, [], R.keys(criteria))); 

console.log('filtered array is: ', data.filter(test)); 

(在本JSBin可用)

要做到這一點沒有圖書館,我將上面的代碼轉換成一個無庫的版本,我T的稍微複雜一些,但仍然可讀:

var test = (function(criteria) { 
    var tests = Object.keys(criteria).reduce(function(tests, key) { 
     var field = criteria[key]; 
     if ('min' in field) {tests.push(function(item) { 
      return item[key] > field.min; 
     });} 
     if ('max' in field) {tests.push(function(item) { 
      return item[key] < field.max; 
     });} 
     return tests; 
    }, []); 
    return function(item) { 
     return tests.every(function(test) {return test(item);}); 
    }; 
}(criteria)); 

console.log('filtered array is: ', data.filter(test)); 

JSBin

在任一版本中,該標準被解析一次以創建一組謂詞功能。這些函數被組合成一個作爲過濾器傳遞的謂詞。

+0

爲了使用過濾功能,我對添加一個20kb的庫有點猶豫,但Ramda看起來很有趣,所以我會研究它。至於你提出的filter + reduce方法,看起來grep會更快一些:http://jsperf.com/grep-vs-reduce-loop。 – 2014-11-23 06:31:35

+0

我絕對不會建議只爲這一功能添加Ramda。這只是爲了演示函數(ish)方法。但請注意,這種方法旨在從數據分析中分離標準的處理過程,這正是我認爲您想要的。你的測試不檢查。 (另外請注意,如果你只使用'grep'的jQuery,它比Ramda更大。:-)) – 2014-11-23 21:10:37