Reputation: 12141
I'm building this Builder pattern and I'd like to merge some of the elements in a list together. But I'd like to do this in a cleaner way. This is what I've come up with so far which works but I'm sure there's a better way to do this.
function FiltersBuilder() {
this.filters = [];
};
FiltersBuilder.prototype.addFilter = function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value
}
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.addRegexFilter = function(options) {
var filter = {
'type': 'regex',
'dimension': options.dimension,
'pattern': options.value
}
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.not = function() {
var not = {
'type': 'not'
};
this.filters.push(not);
return this;
}
FiltersBuilder.prototype.getFilters = function() {
var result = [];
this.filters.forEach(function each(filter, index, theFilters) {
if (filter.type === 'not') {
var filterToMerge = theFilters[index + 1];
var mergedFilter = _.merge(filter, {field: filterToMerge});
result.push(mergedFilter);
} else {
if(index > 0) {
notFilter = theFilters[index - 1];
if(notFilter.type === 'not') {
return;
}
}
result.push(filter);
}
});
return result;
}
var filterBuilder = new FiltersBuilder();
filterBuilder.addFilter({
dimension: '_o',
value: 'origin'
});
filterBuilder.not().addRegexFilter({
dimension: 'cTy',
value: 'value'
});
console.log(filterBuilder.getFilters());
If not
method is called before adding filter, I would like to merge the not
filter with the next element and add that object to the result
list. But if not
is not called before adding filter then don't do anything just add the filter to result
list.
https://jsfiddle.net/9wyqbovu/
Upvotes: 0
Views: 1042
Reputation:
Instead of treating not
as a filter to be added to the list of filters, and processed when you get the filters, treat it as a modifier--a flag. Maintain a not
property on the FiltersBuilder
object, initialized to false
, which a call to FilterBuilders.not
will toggle. On each filter, also add a not
property, which is set by the current value of the not
flag on FilterBuilders
(which is then reset). In other words:
function FiltersBuilder() {
this.filters = [];
this.not = false;
};
FiltersBuilder.prototype.addFilter = function(options) {
var filter = {
'type': 'selector',
'dimension': options.dimension,
'value': options.value,
not: this.not
}
this.not = false;
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.addRegexFilter = function(options) {
var filter = {
'type': 'regex',
'dimension': options.dimension,
'pattern': options.value,
not: this.not
}
this.not = false;
this.filters.push(filter);
return this;
}
FiltersBuilder.prototype.not = function() {
this.not = !this.not;
}
FiltersBuilder.prototype.getFilters = function() {
return this.filters;
}
var filterBuilder = new FiltersBuilder();
filterBuilder.addFilter({
dimension: '_o',
value: 'origin'
});
filterBuilder.not().addRegexFilter({
dimension: 'cTy',
value: 'value'
});
console.log(filterBuilder.getFilters());
If you would prefer to be able to say filterBuilder.not.addFilter
(without the parentheses after not
), then define not
as a getter, ie
Object.defineProperty(FiltersBuilder.prototype, "not", {
get: function() {
this.not = !this.not;
return this;
}
});
Upvotes: 3
Reputation: 14423
Well, how about if we do iterate it from the right and use unshift
instead of push
:
getFilters = function(){
return this.filters.reduceRight(function(results, filter){
if(filter.type === 'not'){
//get filter to 'not'
var notFilter = results[0];
Object.assign(notFilter, filter); //Or _.merge
} else {
results.unshift(filter);
}
return results;
}, []);
}
Alternatively, you can use push
instead of unshift
, grab the last element of the array as the filter to negate (instead of the first one) and reverse the results.
getFilters = function(){
return this.filters.reduceRight(function(results, filter){
if(filter.type === 'not'){
//get filter to 'not'
var notFilter = results[results.length - 1];
Object.assign(notFilter, filter); //Or _.merge
} else {
results.push(filter);
}
return results;
}, []).reverse();
}
Upvotes: 2