some-common-functions-js 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +9 -1
  2. package/README.md +81 -10
  3. package/index.js +157 -10
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -28,4 +28,12 @@ Improve README.md documentation.
28
28
  - Corrected README documentation.
29
29
 
30
30
  ## Version 1.0.8 - 2025/11/21 - ITA
31
- - Corrected test code and README documentation
31
+ - Corrected test code and README documentation.
32
+
33
+ ## Version 1.0.9 - 2025/11/28 - ITA
34
+ - Provided more documentation in test.js to remind a developer how to symbolic-link (mimick as installed) the package and running the test code.
35
+ - Added new function, hasOnlyAll(), and updated the README.md documentation accordingly.
36
+
37
+ ## Version 1.1.0 - 2025/12/22 - ITA
38
+ - Improved documentation.
39
+ - Moved in more functions to the package: `deepClone(anObject)`, `getSortedObject(anObject)`, `timeStampYyyyMmDd(dateInstance)` and `timeStampString(dateInstance)`.
package/README.md CHANGED
@@ -1,13 +1,17 @@
1
- # JavaScript Object Utilities & Field Validation Functions
1
+ # JavaScript Object Utilities, Field Validation Functions, Array Binary Search, and Date timestamp Functions.
2
2
 
3
3
  Common functions used for working with JavaScript objects and validating field values.
4
4
 
5
5
  ---
6
6
  ## Installation
7
- **npm install some-common-functions-js**
8
-
7
+ ```
8
+ npm install some-common-functions-js
9
+ ```
9
10
  ## 1. JavaScript Object Utilities
10
11
 
12
+ ### `deepClone(anObject)`
13
+ Returns a deep clone of a plain Javascript object. The clone, while it has field equal to that of the original object, is separate from the original object.
14
+
11
15
  ### `getPaths(anObject)`
12
16
  Returns a string array of path/field names inside a JavaScript object.
13
17
  ***Example***
@@ -30,8 +34,47 @@ let client = {
30
34
 
31
35
  let paths = getPaths(client);
32
36
  // ["name", "surname", "address.streetNum", "address.streetName", "address.suburb",
33
- // "address.town", "address.country.name", "address.country.code"]
37
+ // "address.town", "address.country.name", "address.country.code"]
34
38
  ```
39
+ ### `getSortedObject(anObject)`
40
+ Returns an object with sorted fields, by ordered by field name ascending.
41
+ ***Examples***
42
+ ```
43
+ const client = {
44
+ firstName: "Isaiah",
45
+ lastName: "Tshabalala",
46
+ address: {
47
+ houseNum: "5520",
48
+ streetName: "Main Road",
49
+ mainPlace: "Evaton",
50
+ subPlace: "Evaton Small Farms",
51
+ city: "Vereeniging",
52
+ country: {
53
+ name: "South Africa",
54
+ code: "ZA"
55
+ }
56
+ }
57
+ };
58
+
59
+ const sortedObject = getSortedObject(client);
60
+ /*
61
+ {
62
+ address: {
63
+ city: 'Vereeniging',
64
+ country: {
65
+ code: 'ZA',
66
+ name: 'South Africa'
67
+ },
68
+ houseNum: '5520',
69
+ mainPlace: 'Evaton',
70
+ streetName: 'Main Road',
71
+ subPlace: 'Evaton Small Farms'
72
+ },
73
+ firstName: 'Isaiah',
74
+ lastName: 'Tshabalala'
75
+ }
76
+ */
77
+ ```
35
78
  ### `hasOnly(anObject, ...fields)`
36
79
  Returns `true` if the object contains **only** some or all of the specified fields and no others.
37
80
  ***Examples***
@@ -73,6 +116,28 @@ let result = hasAll(car, "make", "model");
73
116
 
74
117
  let result = hasAll(car, "passengerCapacity", "year");
75
118
  // false, because car does not have "passengerCapacity" field.
119
+ ```
120
+ ### `hasOnlyAll(anObject, ...fields)`
121
+ Return `true` if an object contains only all the specified fields, nothing more, nothing less
122
+ ***Example***
123
+ ```
124
+ const { hasOnlyAll } = require("some-common-functions-js");
125
+ let car = {
126
+ make: "Ford",
127
+ model: "Ranger",
128
+ year: "2015",
129
+ power: "1000kW",
130
+ type: "pickup truck"
131
+ };
132
+ let result = hasOnlyAll(car, "make", "model");
133
+ // false, because car has all the specified fields and extra fields.
134
+
135
+ let result = hasOnlyAll(car, "passengerCapacity", "year");
136
+ // false, because car does not have "passengerCapacity" field.
137
+
138
+ let result = hasOnlyAll(car, "make", "model", "power", "type");
139
+ // true, because car has all the specified fields, nothing less, nothing more.
140
+
76
141
  ```
77
142
  ---
78
143
 
@@ -109,13 +174,13 @@ Compares two values of the same primitive type, according to the sort direction.
109
174
  ```
110
175
  const { compare } = require("some-common-functions-js");
111
176
 
112
- let x = "Fong Kong";
177
+ let x = "Jiāng Fāng";
113
178
  let y = "Isaiah Tshabalala";
114
- let result = compare(x, y); // -1 because "Fong Kong" is before "Isaiah Tshabalala" in ascending order.
179
+ let result = compare(x, y); // -1 because "Jiāng Fāng" is before "Isaiah Tshabalala" in ascending order.
115
180
  result = compare(y, x);
116
- console.log(result); // 1 because "Isaiah Tshabalala" is after "Fong Kong" in ascending order.
181
+ console.log(result); // 1 because "Isaiah Tshabalala" is after "Jiāng Fāng" in ascending order.
117
182
  result = compare(x, y, 'desc');
118
- console.log(result); // 1 because "Fong Kong" is after "Isaiah Tshabalala" in descending order.
183
+ console.log(result); // 1 because "Jiāng Fāng" is after "Isaiah Tshabalala" in descending order.
119
184
  ```
120
185
  ### `binarySearch(anArray, searchVal, startFrom = 0, arraySortDir = 'asc')`
121
186
  Binary Searches a sorted primitive type array for a value and returns the index.
@@ -322,7 +387,7 @@ let anIndex = binarySearchObj(teamsArray, searchObj, 0, "score desc", "numGames
322
387
 
323
388
  let result = objCompare(searchObj, teamsArray[anIndex], "score desc", "numGames asc"); // 0 -- an object with value { score: 85, numGames: 8} exists at teamsArray[anIndex];
324
389
  ```
325
- ## 4. `getObjArrayWithNoDuplicates(objArray, firstOfDuplicates, ...comparisonFields)`
390
+ ### `getObjArrayWithNoDuplicates(objArray, firstOfDuplicates, ...comparisonFields)`
326
391
  Create an array of objects with duplicates eliminated. Taking only the first or last object from each duplicate set. The input array must be sorted according to the values of comparisonFields.
327
392
  * If firstOfDuplicates === true, then the first element in each set of duplicates is taken.
328
393
  * if firstOfDuplicates === false, then the last element is taken from each set of duplicates.
@@ -360,7 +425,13 @@ console.log(noDuplicatesArray); // Should contain only unique objects according
360
425
  ]
361
426
  */
362
427
  ```
428
+ ## 4. Date Timestamp Functions
429
+ ### `timeStampYyyyMmDd(dateInstance)`
430
+ Converts the date object to a string of the form CCYY-MM-DD
431
+
432
+ ### `timeStampString(dateInstance)`
433
+ Converts a date object to a string of the form CCYY-MM-DDThh:mm:ss.ccc, e.g. '2024-02-25T15:00:25.251'
434
+
363
435
  ---
364
436
  ## License
365
437
  MIT
366
-
package/index.js CHANGED
@@ -2,9 +2,15 @@
2
2
  * Description: Common functions to be put here.
3
3
  * Date Dev Version Description
4
4
  * 2025/11/19 ITA 1.00 Genesis.
5
+ * 2025/11/28 ITA 1.01 Added function hasOnlyAll().
6
+ * 2025/12/22 ITA 1.02 Improved documentation of the functions and moved in more functions.
5
7
  */
6
8
  const loDash = require('lodash');
7
9
 
10
+ /**Return true if userName is valid
11
+ * @param {string} userName
12
+ * @returns {boolean} true if a userName is valid, otherwise false.
13
+ */
8
14
  function isValidUserName(userName) {
9
15
  if (!userName) // The username must be provided.
10
16
  return false;
@@ -14,6 +20,10 @@ function isValidUserName(userName) {
14
20
  }
15
21
  module.exports.isValidUserName = isValidUserName;
16
22
 
23
+ /**Return true if a name is valid
24
+ * @param {string} name
25
+ * @returns {boolean} true if a name is valid, otherwise false.
26
+ */
17
27
  function isValidName(name) {
18
28
  if (!name) // The name must be provided.
19
29
  return false;
@@ -23,6 +33,10 @@ function isValidName(name) {
23
33
  }
24
34
  module.exports.isValidName = isValidName;
25
35
 
36
+ /**Return true if userName is valid
37
+ * @param {string} email
38
+ * @returns {boolean} true if an email is valid, otherwise false.
39
+ */
26
40
  function isValidEmail(email) {
27
41
  if (!email)
28
42
  return false;
@@ -32,6 +46,10 @@ function isValidEmail(email) {
32
46
  }
33
47
  module.exports.isValidEmail = isValidEmail;
34
48
 
49
+ /**Return true if userName is valid
50
+ * @param {string} num phone number
51
+ * @returns {boolean} true if phone number is valid, otherwise false.
52
+ */
35
53
  function isValidPhoneNum(num) {
36
54
  if (!num) // The number must be provided.
37
55
  return false;
@@ -41,6 +59,10 @@ function isValidPhoneNum(num) {
41
59
  }
42
60
  module.exports.isValidPhoneNum = isValidPhoneNum;
43
61
 
62
+ /**Return true if the name of an organisation is valid
63
+ * @param {string} name an organisation name
64
+ * @returns {boolean} true if an organisation name is valid, otherwise false.
65
+ */
44
66
  function isValidOrganisationName(name) {
45
67
  if (!name) // The name must be provided.
46
68
  return false;
@@ -50,6 +72,10 @@ function isValidOrganisationName(name) {
50
72
  }
51
73
  module.exports.isValidOrganisationName = isValidOrganisationName;
52
74
 
75
+ /**Return true if a password is valid
76
+ * @param {string} password
77
+ * @returns {boolean} true if a password is valid, otherwise false.
78
+ */
53
79
  function isValidPassword(password) {
54
80
  if (!password)
55
81
  return false;
@@ -82,9 +108,79 @@ function isValidPassword(password) {
82
108
  }
83
109
  module.exports.isValidPassword = isValidPassword;
84
110
 
111
+ /** Converts the date object to a string of the form CCYY-MM-DD
112
+ * @param {Date} dateObj
113
+ * @returns {string} string of the form CCYY-MM-DD
114
+ */
115
+ function timeStampYyyyMmDd(dateObj) {
116
+ // Convert the date to string form yyyy-mm-dd
117
+ let year = dateObj.getFullYear();
118
+ let month = dateObj.getMonth() + 1;
119
+ month = addLeadingZeros(month, 2);
120
+ let day = dateObj.getDate();
121
+ day = addLeadingZeros(day, 2);
122
+ return `${year}-${month}-${day}`;
123
+ } // function timeStampYYYYMMDd(dateObj) {
124
+ module.exports.timeStampYyyyMmDd = timeStampYyyyMmDd;
125
+
126
+ /** Converts a date object to a string of the form CCYY-MM-DDThh:mm:ss.ccc, e.g. '2024-02-25T15:00:25.251'
127
+ * @param {Date} dateObj
128
+ * @returns {string} a string of the form CCYY-MM-DDThh:mm:ss.ccc.
129
+ */
130
+ function timeStampString(dateObj) {
131
+ let hours = addLeadingZeros(dateObj.getHours(), 2);
132
+ let minutes = addLeadingZeros(dateObj.getMinutes(), 2);
133
+ let seconds = addLeadingZeros(dateObj.getSeconds(), 2);
134
+ let milliSec = addLeadingZeros(dateObj.getMilliseconds(), 3);
135
+ return `${timeStampYyyyMmDd(dateObj)}T${hours}:${minutes}:${seconds}.${milliSec}`;
136
+ } // function timeStampString(dateObj) {
137
+ module.exports.timeStampString = timeStampString;
138
+
139
+
140
+ /** Return a numeric string with trailing zeros.
141
+ * E.g. addLeadingZeros(9, 3) = '009'
142
+ * Inputs:
143
+ * @param {Number} aNumber an integer or integer string.
144
+ * @param {Number} newLength the new length of the resulting string.
145
+ * @returns a string of a number with the specified number of leading zeros.
146
+ */
147
+ function addLeadingZeros(aNumber, newLength) {
148
+
149
+ let newString = aNumber + '';
150
+ const howManyZeros = newLength - newString.length;
151
+ for (let count = 1; count <= howManyZeros; count++)
152
+ newString = '0' + newString;
153
+
154
+ return newString;
155
+ } // function addLeadingZeros(aString, newLength) {
156
+ module.exports.addLeadingZeros = addLeadingZeros;
157
+
158
+ /**Convert numeric input to ZAR currency format string.
159
+ * @param {Number} a number
160
+ * @returns a string of the form R 256,534.00
161
+ */
162
+ function toZarCurrencyFormat(number) {
163
+ const zarCurrencyFormat = new Intl.NumberFormat('en-US', {style: 'currency', currency: 'ZAR'});
164
+ return zarCurrencyFormat.format(number).replace(/ZAR/gi, 'R');
165
+ }
166
+ module.exports.toZarCurrencyFormat = toZarCurrencyFormat;
167
+
168
+ /**Return a deep clone of a document object.
169
+ * By using deep cloning, you create a new object that is entirely separate from the original original.
170
+ * So that whatever you do to that clone, such as deletion of fields, does not affect the original.
171
+ * NB. Class instance types will be converted to plain object types due to stringification.
172
+ * @param {object} obj a plain Javascript object.
173
+ * @returns a Javascript object that is separate from the original object.
174
+ */
175
+ function deepClone(obj) {
176
+ return JSON.parse(JSON.stringify(obj));
177
+ } // function deepClone(obj) { // Return a deep clone of an object.
178
+ module.exports.deepClone = deepClone;
179
+
180
+
85
181
  /**
86
182
  * Get the paths (fields) of the plain Javascript object.
87
- * @param {object} anObject
183
+ * @param {object} anObject a plain Javascript object.
88
184
  * @returns a sorted string array of paths.
89
185
  */
90
186
  function getPaths(anObject) {
@@ -107,11 +203,32 @@ function getPaths(anObject) {
107
203
  } // function getPaths()
108
204
  module.exports.getPaths = getPaths;
109
205
 
206
+ /** Return an object with sorted fields, by ordered by field name ascending.
207
+ * This is desirable when equality comparison is done to ensure two objects sharing equal field values
208
+ * the pass the equality test stringify(object1) === stringify(object2)
209
+ * @param {object} pObject
210
+ * @returns {object} an object with fields sorted in ascending order of field names.
211
+ */
212
+ function getSortedObject(pObject) {
213
+
214
+ const paths = getPaths(pObject);
215
+ paths.sort();
216
+ const sortedObject = {};
217
+
218
+ for (let index in paths) {
219
+ const path = paths[index];
220
+ const value = loDash.get(pObject, path);
221
+ loDash.set(sortedObject, path, value);
222
+ } // for (index in paths) {
223
+ return sortedObject;
224
+ } // function getSortedObject(pObject) {
225
+ module.exports.getSortedObject = getSortedObject;
226
+
110
227
  /**
111
- * Determine whether an object contains only some or all of the specified fields, and not any other fields.
112
- * @param {*} anObject a Javascript object.
228
+ * Determine whether an object contains only 1, some or all of the specified fields, and not any other fields.
229
+ * @param {object} anObject a Javascript object.
113
230
  * @param {...string} fields one or more field names.
114
- * @returns boolean.
231
+ * @returns boolean true or false.
115
232
  */
116
233
  function hasOnly(anObject, ...fields) {
117
234
  if (!fields || !fields.length)
@@ -133,10 +250,10 @@ function hasOnly(anObject, ...fields) {
133
250
  module.exports.hasOnly = hasOnly;
134
251
 
135
252
  /**
136
- * Determine whether an object contains all of the specified fields. It may have additional fields.
137
- * @param {*} anObject a Javascript object.
253
+ * Determine whether an object contains all of the specified fields in addition to other fields.
254
+ * @param {object} anObject a Javascript object.
138
255
  * @param {...string} fields one or field names.
139
- * @returns boolean.
256
+ * @returns boolean true or false.
140
257
  */
141
258
  function hasAll(anObject, ...fields) {
142
259
  if (!fields || !fields.length)
@@ -157,6 +274,16 @@ function hasAll(anObject, ...fields) {
157
274
  }
158
275
  module.exports.hasAll = hasAll;
159
276
 
277
+ /**
278
+ * Determine whether an object contains only all of the specified fields. Nothing more, nothing less.
279
+ * @param {object} anObject a Javascript object.
280
+ * @param {...string} fields one or field names.
281
+ * @returns boolean true or false.
282
+ */
283
+ function hasOnlyAll(anObject, ...fields) {
284
+ return hasOnly(anObject, ...fields) && hasAll(anObject, ...fields);
285
+ }
286
+ module.exports.hasOnlyAll = hasOnlyAll;
160
287
 
161
288
  /**Binary Search the sorted primitive data array for a value and return the index.
162
289
  * ArraySortDir specifies the direction in which the array is sorted (desc or asc).
@@ -164,6 +291,11 @@ module.exports.hasAll = hasAll;
164
291
  * otherwise, the index is of closest value in the array that is before or after the search value in terms of sort order.
165
292
  * Return -1 for an empty array.
166
293
  * This function is to be used also in cases where values are to be inserted into the array while maintaining sort order.
294
+ * @param {Array} anArray an array of primitve type. All element must be the same type.
295
+ * @param {*} searchVal search value
296
+ * @param {number} [startFrom=0] index from which to start. Default: 0.
297
+ * @param {string} [arraySortDir='asc'] sort direction. Must be 'asc' or 'desc'. Default: 'asc'
298
+ * @returns {number} an index
167
299
  */
168
300
  function binarySearch(anArray, searchVal, startFrom = 0, arraySortDir = 'asc') {
169
301
 
@@ -201,7 +333,10 @@ module.exports.binarySearch = binarySearch;
201
333
  * A return value of -1 means that value1 is before value2 in terms of sort order.
202
334
  * A return value of 1 means that value1 is after value2 in terms of sort order.
203
335
  * A return value of 0 means that value1 is equal to value2.
204
- * Sort directions: 'asc', 'desc'. Default is 'asc'.
336
+ * @param {*} value1
337
+ * @param {*} value2
338
+ * @param {string} [sortDir='asc']
339
+ * @returns {number} integer (-1, 0 or 1)
205
340
  */
206
341
  function compare(value1, value2, sortDir = 'asc') {
207
342
  if (!['asc', 'desc'].includes(sortDir))
@@ -225,8 +360,13 @@ module.exports.compare = compare;
225
360
  * Return -1 for an empty array.
226
361
  * Assumed field data types are Number, String and Date.
227
362
  * This function is to be used also in cases where objects are to be inserted into the array while maintaining sort order.
363
+ * @param {Array<object} objArray an array of Javascript objects.
364
+ * @param {object} searchObj an object to search for.
365
+ * @@param {number} [startFrom=0] index from which to start searching.
366
+ * @param {...string} sortFields one or more search fields.
367
+ * @returns {number} an index.
228
368
  */
229
- function binarySearchObj(objArray, searchObj, startFrom, ...sortFields) {
369
+ function binarySearchObj(objArray, searchObj, startFrom = 0, ...sortFields) {
230
370
  if (objArray.length === 0)
231
371
  return -1;
232
372
 
@@ -253,7 +393,7 @@ function binarySearchObj(objArray, searchObj, startFrom, ...sortFields) {
253
393
  } // function binarySearchObj(objArray, searchObj, ...comparisonFields) {
254
394
  module.exports.binarySearchObj = binarySearchObj;
255
395
 
256
- /**Create an array with duplicates eliminated. Taking only the first or last object from each duplicate set.
396
+ /**Create an array with duplicates eliminated, according to certain fields. Taking only the first or last object from each duplicate set.
257
397
  * If firstOfDuplicates === true, then the first element in each set of duplicates is taken.
258
398
  * if firstOfDuplicates === false, then the last element is taken from each set of duplicates.
259
399
  * Assumed field data types are Number, String and Date.
@@ -261,6 +401,10 @@ module.exports.binarySearchObj = binarySearchObj;
261
401
  * The value of the comparison field must include both the field name and sort direction.
262
402
  * Sort direction assumed to be "asc" if not provided.
263
403
  * Examples of comparison fields: "firstName", "lastName desc", "address.province asc", "address.townOrCity".
404
+ * @param {Array<object>} objArray an input array of objects
405
+ * @param {boolean} firstOfDuplicates specify whether to take the first or last object in each a duplicate set.
406
+ * @param {...string} comparisonFields comparison fieds plus sort order.
407
+ * @returns {Array<object>} an array with no duplicates.
264
408
  */
265
409
  function getObjArrayWithNoDuplicates(objArray, firstOfDuplicates, ...comparisonFields) {
266
410
  function getNextSearchObj(pNext) {
@@ -356,6 +500,9 @@ module.exports.getObjArrayWithNoDuplicates = getObjArrayWithNoDuplicates;
356
500
  * Sort directions: 'asc', 'desc'.
357
501
  * Examples: 'lastName desc', 'firstName', 'firstName asc', 'address.provinceName asc'.
358
502
  * If sort direction is not provided, then it is assumed to be ascending.
503
+ * @param {object} obj1 first object to compare
504
+ * @param {object} obj2 second object to compare
505
+ * @returns {number} a comparison value of 1, 0 or -1
359
506
  */
360
507
  function objCompare(obj1, obj2, ...comparisonFields) {
361
508
  if (comparisonFields.length === 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "some-common-functions-js",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Common functions used with Javascript objects, and field validation functions.",
5
5
  "keywords": [
6
6
  "validation",