2012-01-07 8 views
4

私は、単純なコンパレータを使用して、代わりにいくつかのデータを渡すと文字列フィルタを渡すように使用しようとします。 [].filter コンパレータはフィルタとなる関数を返さなければなりません。それは見ていきますこのswitch文を最適化するにはどうすればよいですか?

var filterFn = comparator.apply({}, /(.+)(=|>=|<=|<|>|!=|==|!==)(.+)/.exec("id<4").slice(1)); 


    someModel = someModel.objects.filter(filterFn); 

対象:

var comparator = function(a, b, c) { 
     switch(b){ 
      case '>=': return function() { return this[a] >= c;}; break; 
      case '<=': return function() { return this[a] <= c;}; break; 
      case '<': return function() { return this[a] < c;}; break; 
      case '>': return function() { return this[a] > c;}; break; 
      case '=': return function() { return this[a] == c;}; break; 
      case '==': return function() { return this[a] === c;}; break; 
      case '!=': return function() { return this[a] != c;}; break; 
      default: return null; 
     }; 

    } 

は私がすることで、この機能を入手したと

someModel.get = function(filter){ 
     return new Model( 
      this.objects.filter(
       comparator.apply({}, /(.+)(=|>=|<=|<|>|!=|==|!==)(.+)/.exec("id<4").slice(1) 
      ) 
    ); 
    }; 
    var filtered = someModel.get("id<4"); 

質問です - 私はそれがより多くの事業者になることを想定し、もっと簡単に書く方法はわかりません。

Evalの使用は問題になります。

このコードは実行もテストもされていませんでしたが、私が意味することを示すために書いてあります。

+0

演算子 'c'を既知のホワイトリストの可能な値の配列と比較しても、' eval() 'は問題になりませんか? –

+1

完全な式パーサーが必要ですか。単一の演算子だけではありませんか? – Lucero

+2

各 'return'の後に' break'ステートメントは必要ないことに注意してください。 – nnnnnn

答えて

5

オブジェクト内にすべての関数を定義済みまたは動的に格納します。

機能の集合をダイナミックに作成する場合は、以下のようにcomparatorオブジェクトを定義します。私はあなたがObject.prototypeを拡張しなかったと仮定しました。そうした場合は、最初のループ内でoperators.hasOwnProperty(property)を使用する必要があります。 comparatorが呼び出される

// Run only once 
var funcs = {}; // Optionally, remove `funcs` and swap `funcs` with `operators` 
var operators = { // at the first loop. 
    '>=': '>=', 
    '<=': '<=', 
    '<' : '<', 
    '>' : '>', 
    '=' : '==', //!! 
    '==':'===', //!! 
    '!=': '!=' 
}; // Operators 

// Function constructor used only once, for construction 
for (var operator in operators) { 
    funcs[operator] = Function('a', 'c', 
         'return function() {return this[a] ' + operator + ' c};'); 
} 

// Run later 
var comparator = function(a, b, c) { 
    return typeof funcs[b] === 'function' ? funcs[b](a, c) : null; 
}; 

は、返される関数は次のようになります。

function() { return this[a] < c; }// Where a, c are pre-determined. 
このメソッドは、この方法( demo at JSFiddle)で実施することができる

明確にするため
// Assumed that funcs has been defined 
function implementComparator(set, key, operator, value) { 
    var comparator, newset = [], i; 

    if (typeof funcs[operator] === 'function') { 
     comparator = funcs[operator](key, value); 
    } else { //If the function does not exist... 
     throw TypeError("Unrecognised operator"); 
    } 

    // Walk through the whole set 
    for (i = 0; i < set.length; i++) { 
     // Invoke the comparator, setting `this` to `set[i]`. If true, push item 
     if (comparator.call(set[i])) { 
      newset.push(set[i]); 
     } 
    } 
    return newset; 
} 
var set = [ {meow: 5}, {meow: 3}, {meow: 4}, {meow: 0}, {meow: 9}] 
implementComparator(set , 'meow', '<=', 5); 
// equals: [ {meow: 5}, {meow: 3}, {meow: 4}, {meow: 0} ] 

、I次のことを念頭に置いてこの回答を作成しました:

  • OPは、未知/動的な演算子のセットを持つ、簡単で簡単に拡張可能なメソッドを要求します。
  • コードはOPの擬似コードに基づいており、OPの意図に影響する可能性のあるものは変更しません。いくつかの調整を行うと、この機能はArray.prototype.filterまたはArray.prototype.sortにも使用できます。
  • eval(またはFunction)はcomparator
+0

私は完全に文字列から関数を作成することについて忘れてしまった:)ありがとう。 – abuduba

+1

@abuduba '=='、 '==='、 '!='については確かですか?あなたのコードは、ネゲートされたアイデンティティ** '!==' **演算子を実装していません。また、ソリッド計算機を実装する場合は、別のRegExpを使用してください。現在、あなたのパターンは**任意の**文字を選択します: '(。+)' **存在していれば**演算子も選択しますか? –

+0

これは基本的に 'eval'を使うのと同じです。正確な関数名を使用していないからといって、文字列からコードを作成して実行しているわけではありません。 'function not_eval(str){return eval(str);}を実行します。 } 'も受け入れられますか? –

2

にすべての呼び出しで使用すべきではないので、動的にそれをしないでください....それだけではなく、それぞれよりも、一度の関数を作成するために、はるかに効率的です彼らが呼び出された時には、それが新しい関数を作成し、比較が行われるたびにメモリリークが発生するためです。この時点で

var comparator = { 
    ">=": function(a, b) { return a >= b;},  
    "<=": function(a, b) { return a <= b;}, 
    "add": function(a, b) { return a + b; }, 

compare: function(typeString, a, b){ 
    if(comparator.hasOwnProperty(typeString) == true){ 

     var theFunction = comparator[typeString]; 

     return theFunction(a, b); 
    } 
    else{ 
     alert("typeString '" + typeString + "' not supported."); 
    } 
}, }; 

var test = comparator.compare(">=", 5, 4); 
+0

または 'var test =(5> = 4);' – PointedEars

2
var comparator = (function() { 
    var fns = { 
    '>=': function (a, c) { return a >= c; }, 
    '<=': function (a, c) { return a <= c; }, 
    '<': function (a, c) { return a < c; }, 
    '>': function (a, c) { return a > c; }, 
    '=': function (a, c) { return a == c; }, 
    '==': function (a, c) { return a === c; }, 
    '!=': function (a, c) { return a != c; } 
    }; 

    return function (b) { 
    return fns.hasOwnProperty(b) ? fns[b] : null; 
    }; 
}()); 

あなたは何もインライン表現よりも効率的ではないことがわかります。あなたがあまりにもダイナミックである必要があると思う理由は、私には分かりません。

+0

まあ、オリジナルのコードが大量に見えていない(オペランド*と*演算子が閉鎖されているのですが、なぜインラインではないのでしょうか?)、可変オペランド)。 – PointedEars

関連する問題