1 | [![Build Status](https://travis-ci.org/BlueT/obj-filter.svg?branch=master)](https://travis-ci.org/BlueT/obj-filter)
|
2 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=obj-filter&metric=alert_status)](https://sonarcloud.io/dashboard?id=obj-filter)
|
3 |
|
4 | # obj-filter - JavaScript Object Filter / Merger.
|
5 |
|
6 | JavaScript Object Filter. **Deep** filtering key/content *recursively*.
|
7 | Support **type checking**, **wildcard**, **nested**, and **filter function** in *template*.
|
8 |
|
9 | ## INSTALL
|
10 |
|
11 | `npm i obj-filter`
|
12 |
|
13 | Or find help from:
|
14 | - https://www.npmjs.com/package/obj-filter
|
15 | - https://github.com/BlueT/obj-filter
|
16 |
|
17 | ## SYNOPSIS
|
18 |
|
19 | ~~~~ js
|
20 | "use strict";
|
21 |
|
22 | const {filter, merge, exist, ArrayIter} = require('obj-filter');
|
23 |
|
24 | const template = {
|
25 | "runtime": {
|
26 | "connectionState": undefined, // excluded
|
27 | "powerState": function (args) {return "HELLO WORLD " + args}, // pass into the checker function
|
28 | "bootTime": "my boot time", // included
|
29 | "paused": false,
|
30 | "snapshotInBackground": 1111111,
|
31 | "numbers": ArrayIter(filter, Number), // value of "numbers" must be an array, and will check all elements in the array
|
32 | },
|
33 | "running": Boolean
|
34 | };
|
35 |
|
36 | let clean_data = filter( template, fetchData() );
|
37 | let updated_data = filter.merge( clean_data, newUpdates() );
|
38 | let clean_full_data = filter.exist( template, fetchData() );
|
39 | ~~~~
|
40 |
|
41 | ## Template Object
|
42 | According to the **Template Object structure**, `obj-filter` supports the following types of value with different behaviors to build the result object.
|
43 |
|
44 | ### undefined
|
45 | If the *value* of the key is `undefined`, the key will be **filtered** (skipped) and will not included in result object.
|
46 |
|
47 | ### object
|
48 | If the *value* of the key is an `object`, `obj-filter` will _dive into it and check the **deeper** level of keys_.
|
49 |
|
50 | ### function
|
51 | If the *value* of the key is an `function`, `obj-filter` will _pass the **value** of the same key in **input data** to the **function**_, and includes it's returned data in result.
|
52 | So it's your call to customize how you would like to handle, define what you want to do with the input data. Be sure to **return something** from your function.
|
53 |
|
54 | - If return `undefined`, the key will be **filtered** (skipped).
|
55 | - If return anything else, the key will be **included**.
|
56 |
|
57 | ### DataTypes / Constructors
|
58 | `String`, `Number`, `Boolean`, `Array`, `Symbol`, `Map`, `Set`, `WeakMap`, `WeakSet`, `Object`, `Function` in template will do type checking on target object.
|
59 |
|
60 | Success if type matches and fails if they don't.
|
61 |
|
62 | ### Anything else (string, array, number, etc)
|
63 | The value of the key will be **included**.
|
64 |
|
65 | ### onException callback function
|
66 | You can pass an additional `onException` callback function into `filter()`, `filter.merge()`, and `filter.exist()` to handle exceptions.
|
67 |
|
68 | `onException(template, input, error_msg)` will be called when data expected but type mismatch or undefined.
|
69 |
|
70 | ~~~~ js
|
71 | filter(template, data, (tpl, obj, err) => { console.dir({tpl, obj, err}); return undefined; });
|
72 | ~~~~
|
73 |
|
74 |
|
75 | ## ArrayIter Check Array Elements
|
76 |
|
77 | If you want to check `values of array`, use `ArrayIter`. It makes sure that value must be an Array and checks all elements in the array.
|
78 |
|
79 | The first two arguments are required:
|
80 | - (required) filter / merge / exist
|
81 | - (required) template
|
82 | - (optional) option
|
83 | - min: an Integer, default `0`. indicates at least how many elements must be valid. If the result array contains elements fewer than that number, the whole result will be `undefined`.
|
84 | - onException: a Function, will be called when exception occurred.
|
85 |
|
86 | ~~~~ js
|
87 | const template = {
|
88 | "a1": ArrayIter(filter, {
|
89 | "a": String,
|
90 | "a11": ArrayIter(
|
91 | exist,
|
92 | Number,
|
93 | {"min": 3, onException: () => console.log("WAAATT")}
|
94 | ),
|
95 | "a12": ArrayIter(merge, {
|
96 | "a121": filter.ArrayIter(filter, Number)
|
97 | }),
|
98 | }),
|
99 | "a2": ArrayIter(filter, Number)
|
100 | };
|
101 | ~~~~
|
102 |
|
103 |
|
104 | ## Default Function
|
105 |
|
106 | ### Keep only wanted data
|
107 |
|
108 | When fetching data through API, sometimes the returned data could be Huge. You need many of them, but there are also too many trash included in returned data.
|
109 | Copying with `result[xxx] = input[xxx];` each by each, line by line, is a hell.
|
110 | Now you can copy one returned data structure (in JSON) to your favorite text editor, delete all unwanted lines, paste it back to your code, and use it as template.
|
111 |
|
112 | ~~~~ js
|
113 | "use strict";
|
114 |
|
115 | var filter = require('obj-filter');
|
116 |
|
117 | var template = {
|
118 | "runtime": {
|
119 | "connectionState": undefined, // In Template, when the value is undefined, the key will be ignored.
|
120 | "powerState": function (args) {return "HELLO WORLD " + args}, // pass data into your function, and use it as result value
|
121 | "bootTime": "my boot time", // The string is just for your own note. Will keep whatever input is in result.
|
122 | "paused": false, // Will keep whatever input is in result.
|
123 | "snapshotInBackground": 1111111 // Will keep whatever input is in result.
|
124 | }
|
125 | };
|
126 |
|
127 | var data = function_or_somewhere();
|
128 |
|
129 | // Assume:
|
130 | // var data = {
|
131 | // "vm": {
|
132 | // "type": "VirtualMachine"
|
133 | // },
|
134 | // "runtime": {
|
135 | // "device": 9999,
|
136 | // "connectionState": "connected",
|
137 | // "powerState": "poweredOn",
|
138 | // "bootTime": "2017-04-20T13:56:19.377Z",
|
139 | // "paused": false,
|
140 | // "snapshotInBackground": true
|
141 | // }
|
142 | //};
|
143 |
|
144 |
|
145 | var clean_data = filter(template, data);
|
146 |
|
147 | // clean_data is:
|
148 | {
|
149 | "runtime": {
|
150 | "powerState": "HELLO WORLD poweredOn",
|
151 | "bootTime": "2017-04-20T13:56:19.377Z",
|
152 | "paused": false,
|
153 | "snapshotInBackground": true
|
154 | }
|
155 | };
|
156 | ~~~~
|
157 |
|
158 | ### User Data Checks
|
159 |
|
160 | Validate user input data in browser (before send to server), or check them at server-side.
|
161 |
|
162 | ~~~~ js
|
163 | var template = {
|
164 | email: validateEmail(email), // call function validateEmail and use it's return value as value
|
165 | username: function (username) {
|
166 | if (/^[a-zA-Z_]+$/.test(username)) { // check if username contains only a-z or underscore
|
167 | return username;
|
168 | } else {
|
169 | throw new Error('Invalid username');
|
170 | }
|
171 | },
|
172 | password: "original password" // keep whatever user inputs
|
173 | }
|
174 |
|
175 | save_or_send( filter(template, inputData) );
|
176 | ~~~~
|
177 |
|
178 | ### Separated template file
|
179 |
|
180 | You can save template into separated files.
|
181 |
|
182 | Say _data_template/vmInfo.js_
|
183 |
|
184 | ~~~~ js
|
185 | {
|
186 | "runtime": {
|
187 | "connectionState": undefined,
|
188 | "powerState": function (args) {return "HELLO WORLD " + args},
|
189 | "bootTime": "my boot time",
|
190 | "paused": false,
|
191 | "snapshotInBackground": 1111111
|
192 | }
|
193 | };
|
194 | ~~~~
|
195 |
|
196 | Require it as template
|
197 |
|
198 | ~~~~ js
|
199 | var vm_tpl = require('data_template/vmInfo.js');
|
200 |
|
201 | var vmData = filter(vm_tpl, yourData)
|
202 | ~~~~
|
203 |
|
204 | ## `merge` Function
|
205 |
|
206 | ### Keep template keys when not provided in input data.
|
207 |
|
208 | ~~~~ js
|
209 | "use strict";
|
210 |
|
211 | var filter = require('obj-filter');
|
212 |
|
213 | var template = {
|
214 | "runtime": {
|
215 | "connectionState": undefined,
|
216 | "powerState": function (args) {return "HELLO WORLD " + args},
|
217 | "CoffeeTeaOrMe": "Me"
|
218 | }
|
219 | };
|
220 |
|
221 | var newUpdates = fetchChanges();
|
222 |
|
223 | // Assume:
|
224 | // var newUpdates = {
|
225 | // "runtime": {
|
226 | // "connectionState": "connected",
|
227 | // "powerState": "poweredOn"
|
228 | // }
|
229 | //};
|
230 |
|
231 |
|
232 | var updated_data = filter.merge(template, newUpdates);
|
233 |
|
234 | // updated_data is:
|
235 | {
|
236 | "runtime": {
|
237 | "powerState": "HELLO WORLD poweredOn",
|
238 | "bootTime": "2017-04-20T13:56:19.377Z",
|
239 | "CoffeeTeaOrMe": "Me"
|
240 | }
|
241 | };
|
242 | ~~~~
|
243 |
|
244 |
|
245 | ## `exist` Function
|
246 |
|
247 | ### Similar to default `filter`, but All Keys in template must also exists in input data.
|
248 |
|
249 | ~~~~ js
|
250 | "use strict";
|
251 |
|
252 | var filter = require('obj-filter');
|
253 |
|
254 | var template = {
|
255 | "vm": undefined,
|
256 | "runtime": {
|
257 | "connectionState": undefined,
|
258 | "powerState": function (args) {return "HELLO WORLD " + args},
|
259 | "bootTime": "my boot time",
|
260 | "obj jj": { "kk": "yy" }
|
261 | }
|
262 | };
|
263 |
|
264 | var data = fetch_from_somewhere();
|
265 |
|
266 | // Assume:
|
267 | // var data = {
|
268 | // "runtime": {
|
269 | // "device": 9999,
|
270 | // "connectionState": "connected",
|
271 | // "powerState": "poweredOn",
|
272 | // "bootTime": 2,
|
273 | // "obj jj": { "kk": "zz" }
|
274 | // }
|
275 | // };
|
276 |
|
277 |
|
278 | var clean_full_data = filter.exist(template, data);
|
279 |
|
280 | // clean_full_data is:
|
281 | {
|
282 | "runtime": {
|
283 | "powerState": "HELLO WORLD poweredOn",
|
284 | "bootTime": 2,
|
285 | "obj jj": { "kk": "zz" }
|
286 | }
|
287 | };
|
288 | ~~~~
|
289 |
|
290 | ## Contribute
|
291 |
|
292 | PRs welcome!
|
293 | If you use/like this module, please don't hesitate to give me a **Star**. I'll be happy whole day!
|
294 |
|
295 | _Hope this module can save your time, a tree, and a kitten._
|