2

I'm working in a very simple rules engine that evaluates a series of conditions consisting in an operation and an expected value. The rules are only valid when all conditions in the array are true.

My code (it currently works, actually) looks similar to the following, I just simplified the function bodies to make the question more concise:

const isA = (p1, p2, p3) => {
    return false; // Simplified function body, real thing has some logic
};

const isB = (p1, p2, p3) => {
    return false;
};

const getC = (p1, p2, p3) => {
    return 0;
};

const getD = (p1, p2, p3) => {
    return "string";
};

const rule = {
    conditions: [
        { operation: isA, expectedValue: false },
        { operation: isB, expectedValue: false },
        { operation: getC, expectedValue: 0 },
        { operation: getD, expectedValue: "string" }
    ]
};

function isConditionMet(condition) {
    return (
        condition.operation(this.p1, this.p2, this.p3) ===
        condition.expectedValue
    );
}

export const areRuleConditionsMet = (p1, p2, p3) => {
    return rule.conditions.every(isConditionMet, { p1, p2, p3 });
}

Basically, p1, p2 and p3 are used within the operations to generate an actual value to be compared against the expected value to determine if each individual condition is met and if every of them are met, then the exported function would return true.

What I would like to do is changing my code so that isConditionMet can become an Arrow Function to follow the structure of the rest of the code and also avoid using this but my problem is that, obviously, I depend on the thisArg of Array.prototype.every() to propagate the arguments of areRuleConditionsMet to each operation. Any Ideas?

Luciano M. L.
  • 709
  • 1
  • 5
  • 9
  • 2
    *"Any Ideas?"* Keep it as it is. Not everything has to be an arrow function. Or if `isConditionMet` is kind of fixed (cannot be changed anyway), define it inside `areRuleConditionsMet` and reference `p1`, `p2`, `p3` directly: `return rule.conditions.every(c => c.operation(p1, p2, p3) === c.expectedValue)`. – Felix Kling Mar 16 '20 at 20:39
  • https://stackoverflow.com/questions/33308121/can-you-bind-this-in-an-arrow-function – epascarello Mar 16 '20 at 20:43
  • @epascarello asker wants to *avoid* using `this`, not find another way to use it with arrow functions – Klaycon Mar 16 '20 at 20:44

2 Answers2

0

You can simply pass an anonymous arrow function to Array#every() and make isConditionMet() accept another argument containing p1, p2, and p3.

const isConditionMet = (condition, params) => (
        condition.operation(params.p1, params.p2, params.p3) ===
        condition.expectedValue
);

export const areRuleConditionsMet = (p1, p2, p3) => {
    return rule.conditions.every(condition => isConditionMet(condition, { p1, p2, p3 }));
}
Klaycon
  • 10,599
  • 18
  • 35
  • That is a good point @Klaycon, I used your approach and works as I expected. I also changed isConditionMet to a const + `=>` which is what I wanted my code to look like at the beginning. – Luciano M. L. Mar 16 '20 at 21:06
  • 1
    @LucianoM.L. Right, forgot that was the goal here haha. I edited the answer to match the original goal just for posterity. – Klaycon Mar 16 '20 at 21:08
0

What I often like to use is functions that create functions (also known as currying):

const isConditionMet = (p1, p2, p3) => ({operation, expectedValue}) => 
   operation(p1, p2, p3) === expectedValue

And then you can use it like:

rule.conditions.every(isConditionMet(p1, p2, p3))
Balázs Édes
  • 13,452
  • 6
  • 54
  • 89