Skip to content

Commit d9dffb0

Browse files
authored
feat(filter) Adds code to allow filters to match objects with missing… (Avocarrot#4)
* feat(filter) Adds code to allow filters to match objects with missing and/or empty values * refactor(parseFilter) Avoids check for boolean and instead coerces value to boolean
1 parent 740af9b commit d9dffb0

File tree

4 files changed

+54
-8
lines changed

4 files changed

+54
-8
lines changed

README.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,15 @@ q.or(filters); //returns an array of all objects in `arr` that satisfy SOME of t
7878

7979
## Filters
8080

81-
`field` - The name of the property we will filter on
81+
*`field`* - The name of the property we will filter on
8282

83-
`value` - The value of the property we will filter on
83+
*`value`* - The value of the property we will filter on
8484

85-
`operator` - The filter operator. Supported operators `equals`, `contains`, `gt`, `gte`, `lt`, `lte`, `ne`
85+
*`operator`* - The filter operator. Supported operators `equals`, `contains`, `gt`, `gte`, `lt`, `lte`, `ne`
86+
87+
*`matchMissing`* - If `true` the filter will be satisfied even if `field` property is not present on the object. Default is `false`.
88+
89+
*`matchEmpty`* - If `true` the filter will be satisfied even if `value` property is empty. Empty values are `[]`, `undefined`, `null`, `''` and `{}`. Default is `false`.
8690

8791
## Contributing
8892

lib/filter.js

+29-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,40 @@ exports.parseFilter = (filter) => {
1111
throw new Error('Invalid filter operator');
1212
}
1313

14+
filter.matchMissing = !!filter.matchMissing;
15+
filter.matchEmpty = !!filter.matchEmpty;
16+
1417
return filter;
1518
};
1619

20+
21+
const isEmpty = (val) => {
22+
if (val == null) {
23+
return true;
24+
}
25+
if (Array.isArray(val) || typeof val === 'string') {
26+
return val.length === 0;
27+
}
28+
if (typeof val === 'object') {
29+
return Object.keys(val).length === 0;
30+
}
31+
return false;
32+
};
33+
1734
exports.isSatisfied = (filter, obj) => {
1835
const field = filter.field;
1936
const value = filter.value;
2037
const operator = operators[filter.operator];
21-
return obj.hasOwnProperty(field) ? operator(obj[field], value) : false;
38+
const matchMissing = filter.matchMissing || false;
39+
const matchEmpty = filter.matchEmpty || false;
40+
41+
if (!obj.hasOwnProperty(field)) {
42+
return matchMissing;
43+
}
44+
45+
if (isEmpty(obj[field])) {
46+
return matchEmpty;
47+
}
48+
49+
return operator(obj[field], value);
2250
};

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "query-objects",
3-
"version": "0.0.3",
3+
"version": "0.0.4",
44
"description": "A utility library which filters objects from an array of objects based on a set of filter conditions",
55
"main": "index.js",
66
"scripts": {

test/lib/filter.spec.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,23 @@ describe('Filter module tests', () => {
3232
});
3333

3434
it('isSatisfied(filter, obj) should return true if `filter` is satisfied by `obj` otherwise false', () => {
35-
isSatisfied({field: 'foo', value: 'bar', operator: 'equals'}, {'foo': 'bar'}).should.be.true;
36-
isSatisfied({field: 'foo', value: 'bar', operator: 'equals'}, {'foo': 'baz'}).should.be.false;
37-
isSatisfied({field: 'foo', value: 'bar', operator: 'equals'}, {'fiz': 'bar'}).should.be.false;
35+
let filter = {field: 'foo', value: 'bar', operator: 'equals'};
36+
isSatisfied(filter, {'foo': 'bar'}).should.be.true;
37+
isSatisfied(filter, {'foo': 'baz'}).should.be.false;
38+
isSatisfied(filter, {'fiz': 'bar'}).should.be.false;
39+
40+
// Test `matchMissing`
41+
filter.matchMissing = true;
42+
isSatisfied(filter, {}).should.be.true;
43+
44+
// Test `matchEmpty`
45+
filter.matchEmpty = true;
46+
isSatisfied(filter, {'foo': null}).should.be.true;
47+
isSatisfied(filter, {'foo': []}).should.be.true;
48+
isSatisfied(filter, {'foo': undefined}).should.be.true;
49+
isSatisfied(filter, {'foo': ''}).should.be.true;
50+
isSatisfied(filter, {'foo': {}}).should.be.true;
51+
3852
});
3953

4054
});

0 commit comments

Comments
 (0)