I am trying to emulate Java 8's predicate chaining in JavaScript.
So far it works, but is there a more efficient way to write this? Furthermore, I had to create get
and run
methods to return the predicate so that is can be passed into a filter
function.
Update: I added an _unwrap
method to unwrap the internal predicate function so that you can pass a Predicate
to another Predicate
.
const records = [
{ name: 'Andrew' , age: 21, position: 'Developer' },
{ name: 'Bill' , age: 26, position: 'Developer' },
{ name: 'Chris' , age: 45, position: 'Manager' },
{ name: 'Dave' , age: 30, position: 'Developer' },
{ name: 'Eric' , age: 32, position: 'Manager' }
];
class Predicate {
constructor(predicateFn) {
this.predicateFn = this._unwrap(predicateFn);
}
/* @private */ _unwrap(predicateFn) {
return predicateFn instanceof Predicate ? predicateFn.get() : predicateFn;
}
and(predicateFn) {
return new Predicate((item) => this.predicateFn(item) && this._unwrap(predicateFn)(item));
}
or(predicateFn) {
return new Predicate((item) => this.predicateFn(item) || this._unwrap(predicateFn)(item));
}
not() {
return new Predicate((item) => !this.predicateFn(item));
}
get() {
return this.predicateFn;
}
run(arr) {
return arr.filter(this.predicateFn);
}
}
const display = ({ name, age, position }) => `${name} (${age}) [${position}]`;
const ageOver21AndDeveloper = new Predicate(({ age }) => age > 21)
.and(({ position }) => position === 'Developer');
console.log(ageOver21AndDeveloper.run(records).map(display).join(', '));
const billOrManager = new Predicate(({ name }) => name === 'Bill')
.or(({ position }) => position === 'Manager');
console.log(billOrManager.run(records).map(display).join(', '));
const notUnder30 = new Predicate(({ age }) => age < 30).not();
console.log(notUnder30.run(records).map(display).join(', '));
// Pass predicate object to another predicate.
console.log(billOrManager.or(ageOver21AndDeveloper).run(records).map(display).join(', '));
.as-console-wrapper { top: 0; max-height: 100% !important; }