2016-04-13 5 views
1

Mein Problem besteht darin, eine Zeichenfolge, die eine logische Operation enthält, aufzuteilen. Zum Beispiel, hier ist meine Probe string:Aufteilen einer Zeichenfolge basierend auf UND-ODER-Logik in Javascript

var rule = "device2.temperature > 20 || device2.humidity>68 && device3.temperature >10" 

Ich brauche diese Zeichenfolge in einer Art und Weise zu analysieren, die ich einfach meine Logik arbeiten kann, und ich bin nicht sicher, welcher Ansatz besser wäre.

PS: Bitte beachten Sie, dass diese Regelzeichenfolgen 10 oder mehr verschiedene Bedingungskombinationen haben können, wie 4 UNDs und 6 ODERs.

+0

Warum möchten Sie das tun?Woher kommt die Schnur? Was ist die Logik, die Sie leicht bedienen möchten? Müssen Sie Klammern in Klammern bearbeiten? –

+0

@torazaburo Da wir eine Drittanbieter-Back-End-Software für IOT-Anwendungen namens ThingWorx verwenden, ist ihre Alarmierungsfunktionalität auf ein Gerät beschränkt und wir verfügen nicht über eine geeignete Datenbank, in der wir unsere eigenen Informationen speichern können. Wie auch immer, aus bestimmten eingeschränkten Gründen müssen wir diese Informationen in String-Feldern speichern und die Daten in ThingWorx verarbeiten. Deshalb müssen wir es so halten. –

+0

Dies könnte von Interesse sein, mit Vorlage Zeichenfolgen: http://stackoverflow.com/questions/34882100/can-you-dumb-down-es6-template-strings-to-normal-strings/34883543#34883543 –

Antwort

1

Wenn Sie absolut sicher sind, dass die Eingabe immer gültig JavaScript

var rule = "device2.temperature > 20 || device2.humidity>68 && device3.temperature >10" 
var rulePassed = eval(rule); 

Denken Sie daran, sein wird, dass in den meisten Fällen „eval“ ist „böse“ und hat das Potenzial, mehr Probleme als sie einführen löst.

+0

Also, ich erhalte nur diese Zeichenfolge vom Server, und ich muss Variablen namens device2 und device3 erstellen, und ich werde eine weitere Anfrage an den Server stellen, um aktuelle Temperaturwerte von device2 und device3 abzurufen und dann diese Zeichenfolge schließlich auszuwerten. Mein Problem ist jetzt jedoch, Variablen basierend auf analysierten Strings zu benennen. Also, immer wenn ich "device2" von dieser Zeichenfolge abfange, muss ich "var device2;" Irgendeine Idee, wie ich das tun könnte? –

1
function parse(rule){ 
    return Function("ctx", "return("+rule.replace(/[a-z$_][a-z0-9$_\.]*/gi, "ctx.$&")+")"); 
} 

ein wenig besser als eval, da es wahrscheinlich Fehler werfen wird, wenn Sbd. versucht, etwas Code zu injizieren. Weil es versuchen wird, auf diese Eigenschaften auf dem ctx-Objekt anstelle des window-Objekts zuzugreifen.

var rule = parse("device2.temperature > 20 || device2.humidity>68 && device3.temperature >10"); 
var data = { 
    device2: { 
     temperature: 18, 
     humidity: 70 
    }, 

    device3: { 
     temperature: 15, 
     humidity: 75 
    } 
}; 

console.log(rule.toString()); 
console.log(rule(data)); 
1

keine Klammern Unter der Annahme, könnte ich mit so etwas wie dieses (JavaScript-Code) gehen:

function f(v,op,w){ 
    var ops = { 
    '>': function(a,b){ return a > b; }, 
    '<': function(a,b){ return a < b; }, 
    '||': function(a,b){ return a || b; }, 
    '&&': function(a,b){ return a && b; }, 
    '==': function(a,b){ return a == b;} 
    } 

    if (ops[op]){ 
    return ops[op](v,w); 
    } else alert('Could not recognize the operator, "' + op + '".'); 
} 

Nun, wenn Sie es schaffen, eine Liste von Ausdrücken zu erhalten, können Sie sie in Serie auswerten können:

var exps = [[6,'>',7],'||',[12,'<',22], '&&', [5,'==',5]]; 

var i = 0, 
    result = typeof exps[i] == 'object' ? f(exps[i][0],exps[i][1],exps[i][2]) : exps[i]; 

i++; 

while (exps[i] !== undefined){ 
    var op = exps[i++], 
     b = typeof exps[i] == 'object' ? f(exps[i][0],exps[i][1],exps[i][2]) : exps[i]; 

    result = f(result,op,b); 
    i++; 
} 

console.log(result); 
1

Overkill:

Vorsicht, nicht vollständig getestet. kann immer noch Fehler enthalten
Und, Code überprüft nicht, ob Syntax gültig ist, wirft nur auf ein paar offensichtliche Fehler.

var parse = (function(){  

    function parse(){ 
     var cache = {}; 

     //this may be as evil as eval, so take care how you use it. 
     function raw(v){ return cache[v] || (cache[v] = Function("return " + v)) } 

     //parses Strings and converts them to operator-tokens or functions 
     function parseStrings(v, prop, symbol, number, string){ 
      if(!prop && !symbol && !number && !string){ 
       throw new Error("unexpected/unhandled symbol", v); 
      }else{ 
       var w; 
       switch(prop){ 
        //keywords 
        case "true": 
        case "false": 
        case "null": 
         w = raw(v); 
         break; 
       } 
       tokens.push( 
        w || 
        ~unary.indexOf(prop) && v || 
        prop && parse.fetch(v) || 
        number && raw(number) || 
        string && raw(string) || 
        symbol 
       ); 
      } 
     }  

     var tokens = []; 
     for(var i = 0; i < arguments.length; ++i){ 
      var arg = arguments[i]; 
      switch(typeof arg){ 
       case "number": 
       case "boolean": 
        tokens.push(raw(arg)); 
        break; 

       case "function": 
        tokens.push(arg); 
        break; 

       case "string": 
        //abusing str.replace() as kind of a RegEx.forEach() 
        arg.replace(matchTokens, parseStrings); 
        break; 
      } 
     } 

     for(var i = tokens.lastIndexOf("("), j; i>=0; i = tokens.lastIndexOf("(")){ 
      j = tokens.indexOf(")", i); 
      if(j > 0){ 
       tokens.splice(i, j+1-i, process(tokens.slice(i+1, j))); 
      }else{ 
       throw new Error("mismatching parantheses") 
      } 
     } 
     if(tokens.indexOf(")") >= 0) throw new Error("mismatching parantheses"); 

     return process(tokens); 
    } 

    //combines tokens and functions until a single function is left 
    function process(tokens){ 
     //unary operators like 
     unary.forEach(o => { 
      var i = -1; 
      while((i = tokens.indexOf(o, i+1)) >= 0){ 
       if((o === "+" || o === "-") && typeof tokens[i-1] === "function") continue; 
       tokens.splice(i, 2, parse[ unaryMapping[o] || o ](tokens[i+1])); 
      } 
     }) 
     //binary operators 
     binary.forEach(o => { 
      for(var i = tokens.lastIndexOf(o); i >= 0; i = tokens.lastIndexOf(o)){ 
       tokens.splice(i-1, 3, parse[ o ](tokens[i-1], tokens[i+1])); 
      } 
     }) 

     //ternary operator 
     for(var i = tokens.lastIndexOf("?"), j; i >= 0; i = tokens.lastIndexOf("?")){ 
      if(tokens[i+2] === ":"){ 
       tokens.splice(i-1, 5, parse.ternary(tokens[i-1], tokens[i+1], tokens[i+3])); 
      }else{ 
       throw new Error("unexpected symbol") 
      } 
     } 

     if(tokens.length !== 1){ 
      throw new Error("unparsed tokens left"); 
     } 
     return tokens[0]; 
    } 

    var unary = "!,~,+,-,typeof".split(","); 
    var unaryMapping = { //to avoid collisions with the binary operators 
     "+": "plus", 
     "-": "minus" 
    } 
    var binary = "**,*,/,%,+,-,<<,>>,>>>,<,<=,>,>=,==,!=,===,!==,&,^,|,&&,||".split(","); 
    var matchTokens = /([a-z$_][\.a-z0-9$_]*)|([+\-*/!~^]=*|[\(\)?:]|[<>&|=]+)|(\d+(?:\.\d*)?|\.\d+)|(["](?:\\[\s\S]|[^"])+["]|['](?:\\[\s\S]|[^'])+['])|\S/gi; 

    (function(){ 
     var def = { value: null }; 
     var odp = (k,v) => { def.value = v; Object.defineProperty(parse, k, def) }; 

     unary.forEach(o => { 
      var k = unaryMapping[o] || o; 
      k in parse || odp(k, Function("a", "return function(ctx){ return " + o + "(a(ctx)) }")); 
     }) 

     //most browsers don't support this syntax yet, so I implement this manually 
     odp("**", (a,b) => (ctx) => Math.pow(a(ctx), b(ctx))); 
     binary.forEach(o => { 
      o in parse || odp(o, Function("a,b", "return function(ctx){ return a(ctx) "+o+" b(ctx) }")); 
     }); 

     odp("ternary", (c,t,e) => ctx => c(ctx)? t(ctx): e(ctx)); 

     odp("fetch", key => { 
      var a = key.split("."); 
      return ctx => { 
       //fetches a path, like devices.2.temperature 
       //does ctx["devices"][2]["temperature"]; 
       for(var i=0, v = ctx /*|| window*/; i<a.length; ++i){ 
        if(v == null) return void 0; 
        v = v[a[i]]; 
       } 
       return v; 
      } 
     }); 

     /* some sugar */ 
     var aliases = { 
      "or": "||", 
      "and": "&&", 
      "not": "!" 
     } 
     for(var name in aliases) odp(name, parse[aliases[name]]); 
    })(); 

    return parse; 
})(); 

und Ihr Code:

var data = { 
    device2: { 
     temperature: 18, 
     humidity: 70 
    }, 

    device3: { 
     temperature: 15, 
     humidity: 75 
    } 
}; 

//you get back a function, that expects the context to work on (optional). 
//aka. (in wich context/object is `device2` defined?) 
var rule = parse("device2.temperature > 20 || device2.humidity>68 && device3.temperature >10"); 
console.log("your rule resolved:", rule(data)); 

Zucker:

var rule1 = parse("device2.temperature > 20"); 
var rule2 = parse("device2.humidity>68 && device3.temperature >10"); 

//partials/combining rules to new ones 
//only `and` (a && b), `or` (a || b), `plus` (+value), `minus` (-value) and 'not', (!value) have named aliases 
var rule3 = parse.or(rule1, rule2); 
//but you can access all operators like this 
var rule3 = parse['||'](rule1, rule2); 
//or you can combine functions and strings 
var rule3 = parse(rule1, "||", rule2); 

console.log("(", rule1(data), "||", rule2(data), ") =", rule3(data)); 

//ternary operator and Strings (' and " supported) 
var example = parse(rule1, "? 'device2: ' + device2.temperature : 'device3: ' + device3.temperature"); 
console.log(example(data)) 

Was zu wissen:

-Code Operator Vorrang behandelt und unterstützt runde Klammern

Wenn ein Weg nicht abgeholt wird, ist es die besondere Funktion gibt nicht definiert (keine Fehler hier geworfen)
Zugriff auf Array-Schlüssel in den Pfaden: parse("devices.2.temperature") holt devices[2].temperature

nicht implementiert:

Parsing Arrays und Parsing Function-Calls und alles rund um Wertmodifikation. Diese Engine führt einige Berechnungen durch, erwartet einige Werte und gibt Ihnen einen Wert aus. Nicht mehr und nicht weniger.

+0

Vielen Dank, ich denke, es wird gut funktionieren, sobald ich meine Datenkonfiguration abgeschlossen habe. –

Verwandte Themen