@lowentry/utils 1.19.4 → 1.21.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowentry/utils",
3
- "version": "1.19.4",
3
+ "version": "1.21.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/LeUtils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import FastDeepEqual from 'fast-deep-equal';
2
2
  import CloneDeep from 'clone-deep';
3
- import {ISSET, IS_OBJECT, IS_ARRAY, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY} from './LeTypes.js';
3
+ import {ISSET, IS_OBJECT, IS_ARRAY, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY, ARRAY} from './LeTypes.js';
4
4
 
5
5
 
6
6
  /**
@@ -46,6 +46,75 @@ export const LeUtils = {
46
46
  equals:
47
47
  (value, other) => FastDeepEqual(value, other),
48
48
 
49
+ /**
50
+ * Performs a deep equality comparison between two collections (objects, maps, arrays, etc), sorting on the keys before comparing them.
51
+ *
52
+ * This is useful for comparing objects that have the same properties, but in a different order, and/or in a different collection type (like Maps vs Objects).
53
+ *
54
+ * @param {*} elementsA The elements to compare.
55
+ * @param {*} elementsB The other elements to compare.
56
+ * @param {string[]} [ignoreKeys=[]] An array of keys to ignore when comparing the elements. This is useful for ignoring properties that are not relevant for the comparison.
57
+ * @return {boolean} Returns true if the given values are equivalent, ignoring the order of properties.
58
+ */
59
+ equalsMapLike:
60
+ (() =>
61
+ {
62
+ const sortKeyValueArrays = (pairA, pairB) => LeUtils.compare(pairA[0], pairB[0]);
63
+ return (elementsA, elementsB, ignoreKeys = []) =>
64
+ {
65
+ elementsA = LeUtils.mapToArray(elementsA, (value, key) => [key, value]).sort(sortKeyValueArrays);
66
+ elementsB = LeUtils.mapToArray(elementsB, (value, key) => [key, value]).sort(sortKeyValueArrays);
67
+ ignoreKeys = (typeof ignoreKeys === 'string') ? ARRAY(ignoreKeys) : LeUtils.mapToArray(ignoreKeys);
68
+
69
+ let indexA = 0;
70
+ let indexB = 0;
71
+ while((indexA < elementsA.length) && (indexB < elementsB.length))
72
+ {
73
+ const [mapKey, mapValue] = elementsA[indexA];
74
+ const [ownMapKey, ownMapValue] = elementsB[indexB];
75
+
76
+ const ignoreKeysIncludesMapKey = ignoreKeys.includes(mapKey);
77
+ const ignoreKeysIncludesOwnMapKey = ignoreKeys.includes(ownMapKey);
78
+ if(ignoreKeysIncludesMapKey)
79
+ {
80
+ indexA++;
81
+ if(ignoreKeysIncludesOwnMapKey)
82
+ {
83
+ indexB++;
84
+ }
85
+ continue;
86
+ }
87
+ else if(ignoreKeysIncludesOwnMapKey)
88
+ {
89
+ indexB++;
90
+ continue;
91
+ }
92
+
93
+ if(!LeUtils.equals(mapKey, ownMapKey) || !LeUtils.equals(mapValue, ownMapValue))
94
+ {
95
+ return false;
96
+ }
97
+ indexA++;
98
+ indexB++;
99
+ }
100
+
101
+ while((indexA < elementsA.length) && ignoreKeys.includes(elementsA[indexA][0]))
102
+ {
103
+ indexA++;
104
+ }
105
+ if(indexA < elementsA.length)
106
+ {
107
+ return false;
108
+ }
109
+
110
+ while((indexB < elementsB.length) && ignoreKeys.includes(elementsB[indexB][0]))
111
+ {
112
+ indexB++;
113
+ }
114
+ return (indexB >= elementsB.length);
115
+ };
116
+ })(),
117
+
49
118
  /**
50
119
  * Returns a deep copy of the given value.
51
120
  *
@@ -2919,7 +2988,21 @@ export const LeUtils = {
2919
2988
  isGivenHostPrivate:
2920
2989
  (host) =>
2921
2990
  {
2991
+ host = STRING(host).trim();
2992
+ if(!host)
2993
+ {
2994
+ return false;
2995
+ }
2996
+ try
2997
+ {
2998
+ host = (new URL(host)).hostname;
2999
+ }
3000
+ catch(e)
3001
+ {
3002
+ host = host.split(':')[0];
3003
+ }
2922
3004
  host = STRING(host).trim().toLowerCase();
3005
+
2923
3006
  if((host === 'localhost') || (host === '127.0.0.1'))
2924
3007
  {
2925
3008
  return true;
@@ -2934,195 +3017,6 @@ export const LeUtils = {
2934
3017
  ((parts[0] === '192') && (parts[1] === '168')); // 192.168.0.0 - 192.168.255.255
2935
3018
  },
2936
3019
 
2937
- /**
2938
- * Creates and returns a new TreeSet.
2939
- * A TreeSet is a set of elements, sorted by a comparator.
2940
- * Binary search is used to find elements, which makes it very fast to find elements.
2941
- *
2942
- * The comparator is also used to determine if two values are equal to each other.
2943
- * This way, you can have values that aren't the same be treated as if they are. This can be used to deal with issues such as floating point errors for example.
2944
- *
2945
- * @param {*[]} elements
2946
- * @param {(valueA:*, valueB:*) => number} comparator
2947
- * @returns {{getElements:(()=>*[]), getComparator:(()=>((valueA:*,valueB:*)=>number)), size:(()=>number), isEmpty:(()=>boolean), contains:((value:*)=>boolean), first:(()=>*|undefined), last:(()=>*|undefined), pollFirst:(()=>*|undefined), pollLast:(()=>*|undefined), add:((value:*)=>void), addAll:((values:*)=>void), getEqualValue:((value:*)=>*), getEqualValueOrAdd:((value:*)=>*)}}
2948
- */
2949
- createTreeSet:
2950
- (elements, comparator) =>
2951
- {
2952
- comparator = comparator || LeUtils.compare;
2953
- elements = elements || [];
2954
- elements.sort(comparator);
2955
-
2956
- /**
2957
- * Performs a binary search on the elements, and returns the result.
2958
- *
2959
- * @param {*} value
2960
- * @returns {{found: boolean, index: number, value: *|undefined}}
2961
- */
2962
- const binarySearch = (value) =>
2963
- {
2964
- let low = 0;
2965
- let high = elements.length - 1;
2966
- while(low <= high)
2967
- {
2968
- const mid = Math.floor((low + high) / 2);
2969
- const midValue = elements[mid];
2970
- const cmp = comparator(midValue, value);
2971
- if(cmp < 0)
2972
- {
2973
- low = mid + 1;
2974
- }
2975
- else if(cmp > 0)
2976
- {
2977
- high = mid - 1;
2978
- }
2979
- else
2980
- {
2981
- return {found:true, index:mid, value:midValue};
2982
- }
2983
- }
2984
- return {found:false, index:low, value:undefined};
2985
- };
2986
-
2987
- const treeSet = {
2988
- /**
2989
- * Returns the elements of the set.
2990
- *
2991
- * @returns {*[]}
2992
- */
2993
- getElements:
2994
- () => elements,
2995
-
2996
- /**
2997
- * Returns the comparator of the set.
2998
- *
2999
- * @returns {(valueA:*, valueB:*) => number}
3000
- */
3001
- getComparator:
3002
- () => comparator,
3003
-
3004
- /**
3005
- * Returns the size of the set.
3006
- *
3007
- * @returns {number}
3008
- */
3009
- size:
3010
- () => elements.length,
3011
-
3012
- /**
3013
- * Returns true if the set is empty, false otherwise.
3014
- *
3015
- * @returns {boolean}
3016
- */
3017
- isEmpty:
3018
- () => (elements.length <= 0),
3019
-
3020
- /**
3021
- * Returns true if the set contains a value that is equal to the given value, returns false otherwise.
3022
- *
3023
- * @param {*} value
3024
- * @returns {boolean}
3025
- */
3026
- contains:
3027
- (value) => binarySearch(value).found,
3028
-
3029
- /**
3030
- * Returns the first element of the set, or undefined if it is empty.
3031
- *
3032
- * @returns {*|undefined}
3033
- */
3034
- first:
3035
- () => (elements.length > 0) ? elements[0] : undefined,
3036
-
3037
- /**
3038
- * Returns the last element of the set, or undefined if it is empty.
3039
- *
3040
- * @returns {*|undefined}
3041
- */
3042
- last:
3043
- () => (elements.length > 0) ? elements[elements.length - 1] : undefined,
3044
-
3045
- /**
3046
- * Removes and returns the first element of the set, or undefined if it is empty.
3047
- *
3048
- * @returns {*|undefined}
3049
- */
3050
- pollFirst:
3051
- () => (elements.length > 0) ? elements.splice(0, 1)[0] : undefined,
3052
-
3053
- /**
3054
- * Removes and returns the last element of the set, or undefined if it is empty.
3055
- *
3056
- * @returns {*|undefined}
3057
- */
3058
- pollLast:
3059
- () => (elements.length > 0) ? elements.splice(elements.length - 1, 1)[0] : undefined,
3060
-
3061
- /**
3062
- * Adds the given value to the set. Will only do so if no equal value already exists.
3063
- *
3064
- * @param {*} value
3065
- */
3066
- add:
3067
- (value) =>
3068
- {
3069
- const result = binarySearch(value);
3070
- if(result.found)
3071
- {
3072
- return;
3073
- }
3074
- elements.splice(result.index, 0, value);
3075
- },
3076
-
3077
- /**
3078
- * Adds all the given values to the set. Will only do so if no equal value already exists.
3079
- *
3080
- * @param {*} values
3081
- */
3082
- addAll:
3083
- (values) =>
3084
- {
3085
- LeUtils.each(values, treeSet.add);
3086
- },
3087
-
3088
- /**
3089
- * Returns an equal value that's already in the tree set, or undefined if no equal values in it exist.
3090
- *
3091
- * @param {*} value
3092
- * @returns {*|undefined}
3093
- */
3094
- getEqualValue:
3095
- (value) =>
3096
- {
3097
- const result = binarySearch(value);
3098
- if(result.found)
3099
- {
3100
- return result.value;
3101
- }
3102
- return undefined;
3103
- },
3104
-
3105
- /**
3106
- * Returns an equal value that's already in the tree set. If no equal values in it exist, the given value will be added and returned.
3107
- *
3108
- * @param {*} value
3109
- * @returns {*}
3110
- */
3111
- getEqualValueOrAdd:
3112
- (value) =>
3113
- {
3114
- const result = binarySearch(value);
3115
- if(result.found)
3116
- {
3117
- return result.value;
3118
- }
3119
- elements.splice(result.index, 0, value);
3120
- return value;
3121
- },
3122
- };
3123
- return treeSet;
3124
- },
3125
-
3126
3020
  /**
3127
3021
  * @typedef {Object} LeUtils_TransactionalValue
3128
3022
  * @property {*} value
@@ -0,0 +1,235 @@
1
+ import {LeUtils} from '../LeUtils.js';
2
+
3
+
4
+ /**
5
+ * A TreeSet is a set of elements, sorted by a comparator.
6
+ * Binary search is used to find elements, which makes it very fast to find elements.
7
+ *
8
+ * The comparator is also used to determine if two values are equal to each other.
9
+ * This way, you can have values that aren't the same be treated as if they are. This can be used to deal with issues such as floating point errors for example.
10
+ */
11
+ export class TreeSet
12
+ {
13
+ /** @type {*[]} */
14
+ #elements;
15
+
16
+ /** @type {(valueA:*, valueB:*) => number} */
17
+ #comparator;
18
+
19
+
20
+ /**
21
+ * Creates a new TreeSet with the given elements and comparator.
22
+ *
23
+ * @param {*[]} [elements=[]] - The initial elements of the set.
24
+ * @param {(valueA:*, valueB:*) => number} [comparator=LeUtils.compare] - The comparator function to use for sorting.
25
+ */
26
+ constructor(elements, comparator)
27
+ {
28
+ this.#comparator = comparator || LeUtils.compare;
29
+ this.#elements = elements || [];
30
+ this.#elements.sort(this.#comparator);
31
+ }
32
+
33
+
34
+ /**
35
+ *
36
+ *
37
+ * @param {*} value - The value to search for in the set.
38
+ * @returns {{found:boolean, index:number, value:*|undefined}} - An object containing whether the value was found, the index where it would be inserted, and the value at that index (if found).
39
+ * @private
40
+ */
41
+ binarySearch(value)
42
+ {
43
+ let low = 0;
44
+ let high = this.#elements.length - 1;
45
+ while(low <= high)
46
+ {
47
+ const mid = Math.floor((low + high) / 2);
48
+ const midValue = this.#elements[mid];
49
+ const cmp = this.#comparator(midValue, value);
50
+ if(cmp < 0)
51
+ {
52
+ low = mid + 1;
53
+ }
54
+ else if(cmp > 0)
55
+ {
56
+ high = mid - 1;
57
+ }
58
+ else
59
+ {
60
+ return {found:true, index:mid, value:midValue};
61
+ }
62
+ }
63
+ return {found:false, index:low, value:undefined};
64
+ };
65
+
66
+
67
+ /**
68
+ * Returns the elements of the set.
69
+ */
70
+ getElements()
71
+ {
72
+ return this.#elements;
73
+ }
74
+
75
+ /**
76
+ * Returns the comparator of the set.
77
+ *
78
+ * @returns {(valueA:*, valueB:*) => number}
79
+ */
80
+ getComparator()
81
+ {
82
+ return this.#comparator;
83
+ }
84
+
85
+ /**
86
+ * Returns the size of the set.
87
+ *
88
+ * @returns {number}
89
+ */
90
+ size()
91
+ {
92
+ return this.#elements.length;
93
+ }
94
+
95
+ /**
96
+ * Returns true if the set is empty, false otherwise.
97
+ *
98
+ * @returns {boolean}
99
+ */
100
+ isEmpty()
101
+ {
102
+ return (this.#elements.length <= 0);
103
+ }
104
+
105
+ /**
106
+ * Returns true if the set contains a value that is equal to the given value, returns false otherwise.
107
+ *
108
+ * @param {*} value - The value to check for in the set.
109
+ * @return {boolean} - True if the set contains the value, false otherwise.
110
+ */
111
+ contains(value)
112
+ {
113
+ return this.binarySearch(value).found;
114
+ }
115
+
116
+ /**
117
+ * Returns the first element of the set, or undefined if it is empty.
118
+ *
119
+ * @return {*|undefined} - The first element of the set, or undefined if it is empty.
120
+ */
121
+ first()
122
+ {
123
+ return (this.#elements.length > 0) ? this.#elements[0] : undefined;
124
+ }
125
+
126
+ /**
127
+ * Returns the last element of the set, or undefined if it is empty.
128
+ *
129
+ * @return {*|undefined} - The last element of the set, or undefined if it is empty.
130
+ */
131
+ last()
132
+ {
133
+ return (this.#elements.length > 0) ? this.#elements[this.#elements.length - 1] : undefined;
134
+ }
135
+
136
+ /**
137
+ * Removes and returns the first element of the set, or undefined if it is empty.
138
+ *
139
+ * @returns {*|undefined} - The first element of the set, or undefined if it is empty.
140
+ */
141
+ pollFirst()
142
+ {
143
+ return (this.#elements.length > 0) ? this.#elements.splice(0, 1)[0] : undefined;
144
+ }
145
+
146
+ /**
147
+ * Removes and returns the last element of the set, or undefined if it is empty.
148
+ *
149
+ * @returns {*|undefined} - The last element of the set, or undefined if it is empty.
150
+ */
151
+ pollLast()
152
+ {
153
+ return (this.#elements.length > 0) ? this.#elements.splice(this.#elements.length - 1, 1)[0] : undefined;
154
+ }
155
+
156
+ /**
157
+ * Adds the given value to the set. Will only do so if no equal value already exists.
158
+ * @param {*} value - The value to add to the set.
159
+ */
160
+ add(value)
161
+ {
162
+ const result = this.binarySearch(value);
163
+ if(result.found)
164
+ {
165
+ return;
166
+ }
167
+ this.#elements.splice(result.index, 0, value);
168
+ }
169
+
170
+ /**
171
+ * Adds all the given values to the set. Will only do so if no equal value already exists.
172
+ *
173
+ * @param {*} values - The values to add to the set.
174
+ */
175
+ addAll(values)
176
+ {
177
+ LeUtils.each(values, this.add.bind(this));
178
+ }
179
+
180
+ /**
181
+ * Returns an equal value that's already in the tree set, or undefined if no equal values in it exist.
182
+ *
183
+ * @param {*} value - The value to search for in the set.
184
+ * @return {*|undefined} - The equal value if found, or undefined if not found.
185
+ */
186
+ getEqualValue(value)
187
+ {
188
+ const result = this.binarySearch(value);
189
+ if(result.found)
190
+ {
191
+ return result.value;
192
+ }
193
+ return undefined;
194
+ }
195
+
196
+ /**
197
+ * Returns an equal value that's already in the tree set. If no equal values in it exist, the given value will be added and returned.
198
+ *
199
+ * @param {*} value - The value to search for in the set.
200
+ * @return {*} - The equal value if found, or the added value if not found.
201
+ */
202
+ getEqualValueOrAdd(value)
203
+ {
204
+ const result = this.binarySearch(value);
205
+ if(result.found)
206
+ {
207
+ return result.value;
208
+ }
209
+ this.#elements.splice(result.index, 0, value);
210
+ return value;
211
+ }
212
+
213
+ /**
214
+ * Returns a string representation of the TreeSet.
215
+ *
216
+ * @returns {string} - A string representation of the TreeSet, including its elements and comparator.
217
+ */
218
+ toString()
219
+ {
220
+ return `TreeSet{elements:${this.#elements}, comparator:${this.#comparator}}`;
221
+ }
222
+
223
+ /**
224
+ * Returns a JSON representation of the TreeSet.
225
+ *
226
+ * @returns {Object} - An object containing the elements and comparator of the TreeSet.
227
+ */
228
+ toJSON()
229
+ {
230
+ return {
231
+ elements: this.#elements,
232
+ comparator:this.#comparator.toString(),
233
+ };
234
+ }
235
+ }
package/src/index.js CHANGED
@@ -2,3 +2,4 @@ export {LeUtils} from './LeUtils.js';
2
2
  export {ISSET, IS_ARRAY, ARRAY, IS_OBJECT, OBJECT, STRING, STRING_ANY, INT, INT_ANY, FLOAT, FLOAT_ANY, INT_LAX, INT_LAX_ANY, FLOAT_LAX, FLOAT_LAX_ANY} from './LeTypes.js';
3
3
  export {LinkedList} from './classes/LinkedList.js';
4
4
  export {SerializableMap} from './classes/SerializableMap.js';
5
+ export {TreeSet} from './classes/TreeSet.js';