@lowentry/utils 1.19.3 → 1.20.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.3",
3
+ "version": "1.20.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
  /**
@@ -43,7 +43,77 @@ export const LeUtils = {
43
43
  * @param {*} other The other value to compare.
44
44
  * @returns {boolean} Returns true if the values are equivalent.
45
45
  */
46
- equals:FastDeepEqual,
46
+ equals:
47
+ (value, other) => FastDeepEqual(value, other),
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
+ })(),
47
117
 
48
118
  /**
49
119
  * Returns a deep copy of the given value.
@@ -0,0 +1,216 @@
1
+ import {describe, test, expect} from 'vitest';
2
+ import {LeUtils, SerializableMap} from '../src/index.js';
3
+
4
+
5
+ const KEY_TO_IGNORE = '@ignored@';
6
+
7
+ const areMapsEqual = (map1, map2) => LeUtils.equalsMapLike(map1, map2);
8
+ const areMapsEqualIgnoreKey = (map1, map2) => LeUtils.equalsMapLike(map1, map2, [KEY_TO_IGNORE]);
9
+
10
+
11
+ describe('testing map comparisons', () =>
12
+ {
13
+ test('map compare true - different classes', () =>
14
+ {
15
+ const ownMap = new SerializableMap([['key1', 'value1'], ['key2', 'value2']]);
16
+ const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
17
+ expect(areMapsEqual(ownMap, map)).toBe(true);
18
+ });
19
+ test('map compare true', () =>
20
+ {
21
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
22
+ const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
23
+ expect(areMapsEqual(ownMap, map)).toBe(true);
24
+ });
25
+ test('map compare false', () =>
26
+ {
27
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
28
+ const map = new Map([['key1', 'value1'], ['key2', 'value3']]);
29
+ expect(areMapsEqual(ownMap, map)).toBe(false);
30
+ });
31
+ test('map compare true - different order', () =>
32
+ {
33
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
34
+ const map = new Map([['key2', 'value2'], ['key1', 'value1']]);
35
+ expect(areMapsEqual(ownMap, map)).toBe(true);
36
+ });
37
+ test('map compare false - different order', () =>
38
+ {
39
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
40
+ const map = new Map([['key2', 'value3'], ['key1', 'value1']]);
41
+ expect(areMapsEqual(ownMap, map)).toBe(false);
42
+ });
43
+ test('map compare true - with location', () =>
44
+ {
45
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
46
+ const map = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
47
+ expect(areMapsEqual(ownMap, map)).toBe(true);
48
+ });
49
+ test('map compare false - with location', () =>
50
+ {
51
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
52
+ const map = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '456']]);
53
+ expect(areMapsEqual(ownMap, map)).toBe(false);
54
+ });
55
+ test('map compare true - ignore location 1', () =>
56
+ {
57
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
58
+ const map = new Map([['key1', 'value1'], ['key2', 'value2']]);
59
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
60
+ });
61
+ test('map compare true - ignore location 2', () =>
62
+ {
63
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
64
+ const map = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
65
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
66
+ });
67
+ test('map compare false - ignore location 1', () =>
68
+ {
69
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
70
+ const map = new Map([['key1', 'value1'], ['key2', 'value3']]);
71
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
72
+ });
73
+ test('map compare false - ignore location 2', () =>
74
+ {
75
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
76
+ const map = new Map([['key1', 'value1'], ['key2', 'value3'], [KEY_TO_IGNORE, '123']]);
77
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
78
+ });
79
+ test('map compare true - empty map', () =>
80
+ {
81
+ const ownMap = new Map();
82
+ const map = new Map();
83
+ expect(areMapsEqual(ownMap, map)).toBe(true);
84
+ });
85
+ test('map compare false - empty map', () =>
86
+ {
87
+ const ownMap = new Map([['key1', 'value1']]);
88
+ const map = new Map();
89
+ expect(areMapsEqual(ownMap, map)).toBe(false);
90
+ });
91
+ test('map compare true - empty map with location', () =>
92
+ {
93
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
94
+ const map = new Map([[KEY_TO_IGNORE, '123']]);
95
+ expect(areMapsEqual(ownMap, map)).toBe(true);
96
+ });
97
+ test('map compare false - empty map with location', () =>
98
+ {
99
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
100
+ const map = new Map([[KEY_TO_IGNORE, '456']]);
101
+ expect(areMapsEqual(ownMap, map)).toBe(false);
102
+ });
103
+ test('map compare true - empty map ignore location', () =>
104
+ {
105
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
106
+ const map = new Map();
107
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
108
+ });
109
+ test('map compare false - empty map ignore location', () =>
110
+ {
111
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
112
+ const map = new Map([['key1', 'value1']]);
113
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
114
+ });
115
+ });
116
+
117
+
118
+ describe('testing map-object comparisons', () =>
119
+ {
120
+ test('map-object compare true', () =>
121
+ {
122
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
123
+ const map = {'key1':'value1', 'key2':'value2'};
124
+ expect(areMapsEqual(ownMap, map)).toBe(true);
125
+ });
126
+ test('map-object compare false', () =>
127
+ {
128
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
129
+ const map = {'key1':'value1', 'key2':'value3'};
130
+ expect(areMapsEqual(ownMap, map)).toBe(false);
131
+ });
132
+ test('map-object compare true - different order', () =>
133
+ {
134
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
135
+ const map = {'key2':'value2', 'key1':'value1'};
136
+ expect(areMapsEqual(ownMap, map)).toBe(true);
137
+ });
138
+ test('map-object compare false - different order', () =>
139
+ {
140
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
141
+ const map = {'key2':'value3', 'key1':'value1'};
142
+ expect(areMapsEqual(ownMap, map)).toBe(false);
143
+ });
144
+ test('map-object compare true - with location', () =>
145
+ {
146
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
147
+ const map = {'key1':'value1', 'key2':'value2', [KEY_TO_IGNORE]:'123'};
148
+ expect(areMapsEqual(ownMap, map)).toBe(true);
149
+ });
150
+ test('map-object compare false - with location', () =>
151
+ {
152
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
153
+ const map = {'key1':'value1', 'key2':'value2', [KEY_TO_IGNORE]:'456'};
154
+ expect(areMapsEqual(ownMap, map)).toBe(false);
155
+ });
156
+ test('map-object compare true - ignore location 1', () =>
157
+ {
158
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
159
+ const map = {'key1':'value1', 'key2':'value2'};
160
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
161
+ });
162
+ test('map-object compare true - ignore location 2', () =>
163
+ {
164
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
165
+ const map = {'key1':'value1', 'key2':'value2', [KEY_TO_IGNORE]:'123'};
166
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
167
+ });
168
+ test('map-object compare false - ignore location 1', () =>
169
+ {
170
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2'], [KEY_TO_IGNORE, '123']]);
171
+ const map = {'key1':'value1', 'key2':'value3'};
172
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
173
+ });
174
+ test('map-object compare false - ignore location 2', () =>
175
+ {
176
+ const ownMap = new Map([['key1', 'value1'], ['key2', 'value2']]);
177
+ const map = {'key1':'value1', 'key2':'value3', [KEY_TO_IGNORE]:'123'};
178
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
179
+ });
180
+ test('map-object compare true - empty map', () =>
181
+ {
182
+ const ownMap = new Map();
183
+ const map = {};
184
+ expect(areMapsEqual(ownMap, map)).toBe(true);
185
+ });
186
+ test('map-object compare false - empty map', () =>
187
+ {
188
+ const ownMap = new Map([['key1', 'value1']]);
189
+ const map = {};
190
+ expect(areMapsEqual(ownMap, map)).toBe(false);
191
+ });
192
+ test('map-object compare true - empty map with location', () =>
193
+ {
194
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
195
+ const map = {[KEY_TO_IGNORE]:'123'};
196
+ expect(areMapsEqual(ownMap, map)).toBe(true);
197
+ });
198
+ test('map-object compare false - empty map with location', () =>
199
+ {
200
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
201
+ const map = {[KEY_TO_IGNORE]:'456'};
202
+ expect(areMapsEqual(ownMap, map)).toBe(false);
203
+ });
204
+ test('map-object compare true - empty map ignore location', () =>
205
+ {
206
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
207
+ const map = {};
208
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(true);
209
+ });
210
+ test('map-object compare false - empty map ignore location', () =>
211
+ {
212
+ const ownMap = new Map([[KEY_TO_IGNORE, '123']]);
213
+ const map = {'key1':'value1'};
214
+ expect(areMapsEqualIgnoreKey(ownMap, map)).toBe(false);
215
+ });
216
+ });