5

I have a library that have the feature of filtering objects based on some fields (each field has a specific kind of type - more info on https://github.com/jy95/mediaScan/wiki )

The biggest problem is to handle number properties from multiple source

// to handle number operations
export interface NumberExpressionObject {
    operator: "==" | ">" | "<" | ">=" | "<=";
    number: number;
}

// additional Properties
export interface AdditionalProperties {
    type: AdditionalPropertiesType;
    name: string;
    value: boolean | string | string[] | number | NumberSearchSyntax;
}

For example :

expect((libInstance.filterTvSeries({
            additionalProperties: [
                {type: "number", name: "whateverFieldThatDoesn'tExist", value: "<50"},
                {type: "number", name: "AnotherField", value: undefined},
                {type: "number", name: "AnotherField2", value: "<=25"},
                {type: "number", name: "AnotherField3", value: ">25"},
                {type: "number", name: "AnotherField4", value: "==25"},
            ],
            season: ">=4",
        }))).toEqual(
            new Map(),
        );

Each one must first corresponds the following regex :

const validExpression = /^(==|>|<|>=|<=)(\d+)$/;

and then will be eval by this function :

function resolveExpression(property: string,
                           expressionObject: MediaScanTypes.NumberExpressionObject,
                           object: MediaScanTypes.TPN | MediaScanTypes.TPN_Extended): boolean {
    return eval(`${object[property]}${expressionObject.operator}${expressionObject.number}`);
}

I wonder what could be a more cleaner way to do that ... Please avoid easy answer like a switch case : I tested it but it was still slower in my tests than eval ...

Function constructor is for me the same thing than eval ..

Previous posts I read :
Evaluating a string as a mathematical expression in JavaScript
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
...

jy95
  • 773
  • 13
  • 36
  • 1
    What do you mean by cleaner? It seems pretty clean to me, you want faster? – JohanP Apr 10 '18 at 01:53
  • 1
    Tslint and Eslint really hate eval and the code here could be mysterious for not advanced js programmers ^^ – jy95 Apr 10 '18 at 01:59

2 Answers2

2
  1. implement functions for operators

    ops = { '>': (a, b) => a > b, '>=': (a, b) => a >= b };

    ops[expressionObject.operator](object[property], expressionObject.number)

  2. if the expression is always valid as expected. then following should faster as no parsing.

    eval(${object[property]}${expression})

aaron.xiao
  • 198
  • 8
  • 1
    Not a bad idea ^^ Just for fun, It could be interesting to do a benchemark for future readers :) (even if I know that eval is a monster ^^) – jy95 Apr 10 '18 at 02:02
2

You can have some mapping from operator to function

const ops = {'==':(a,b)=> a == b};
return ops[${expressionObject.operator}](${object[property]},${expressionObject.number})
JohanP
  • 5,252
  • 2
  • 24
  • 34
  • 1
    thanks for the answer. aaron.xiao was the fastest one but no worry, you got your +1 :) – jy95 Apr 10 '18 at 02:05