@valkyriestudios/utils 12.42.0 → 12.43.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.
- package/README.md +26 -7
- package/array/dedupe.d.ts +10 -8
- package/array/is.d.ts +1 -1
- package/array/isNotEmpty.d.ts +1 -1
- package/caching/memoize.d.ts +1 -1
- package/cjs/array/dedupe.js +27 -39
- package/cjs/caching/memoize.js +3 -3
- package/cjs/hash/djb2.js +14 -0
- package/cjs/hash/fnv1A.js +5 -42
- package/cjs/hash/utils.js +40 -0
- package/esm/array/dedupe.js +27 -39
- package/esm/caching/memoize.js +3 -3
- package/esm/hash/djb2.js +11 -0
- package/esm/hash/fnv1A.js +5 -42
- package/esm/hash/utils.js +37 -0
- package/hash/djb2.d.ts +7 -0
- package/hash/fnv1A.d.ts +1 -1
- package/hash/utils.d.ts +6 -0
- package/index.d.ts +23 -11
- package/object/is.d.ts +1 -1
- package/object/isNotEmpty.d.ts +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -239,7 +239,7 @@ const group = groupBy([
|
|
|
239
239
|
**Take note**: any object without the key will be added to a fallback group called '_'
|
|
240
240
|
|
|
241
241
|
|
|
242
|
-
### array/dedupe(val:Array, opts?:{filter_fn})
|
|
242
|
+
### array/dedupe(val:Array, opts?:{key:string; filter_fn})
|
|
243
243
|
Remove all duplicates from an array, behind the scenes it uses the fnv 1A hash algorithm to performantly do comparisons.
|
|
244
244
|
```typescript
|
|
245
245
|
import dedupe from '@valkyriestudios/utils/array/dedupe';
|
|
@@ -251,6 +251,22 @@ dedupe(['hello', 'hello', 'world']); // ['hello', 'world']
|
|
|
251
251
|
dedupe(['hello', 'hello', 'world', false, 'world'], {filter_fn: el => isNotEmptyString(el)}); // ['hello', 'world']
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
+
Also supports deduping according to a specific `key`:
|
|
255
|
+
```typescript
|
|
256
|
+
import dedupe from '@valkyriestudios/utils/array/dedupe';
|
|
257
|
+
|
|
258
|
+
const users = [
|
|
259
|
+
{ id: 1, name: 'Alice' },
|
|
260
|
+
{ id: 2, name: 'Bob' },
|
|
261
|
+
{ id: 1, name: 'Alicia' }, // duplicate id
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
// Deduplicate by `id`
|
|
265
|
+
const uniqueUsers = dedupe(users, { key: 'id' });
|
|
266
|
+
console.log(uniqueUsers);
|
|
267
|
+
// => [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" } ]
|
|
268
|
+
```
|
|
269
|
+
|
|
254
270
|
Take Note: The filtering is applied while deduping, ensuring O(n) performance, as such this is faster than dedupe(arr.filter(...))
|
|
255
271
|
|
|
256
272
|
### array/join(val:Array, opts:object={delim:' ',trim:true,valtrim:true,innertrim:true,valround:false,dedupe:false})
|
|
@@ -978,15 +994,18 @@ import guid from '@valkyriestudios/utils/hash/guid';
|
|
|
978
994
|
guid(); // 245caf1a-86af-11e7-bb31-be2e44b06b34
|
|
979
995
|
```
|
|
980
996
|
|
|
997
|
+
### hash/djb2(val:unknown)
|
|
998
|
+
Generate a djb2 hash from an object/array/primitive/...
|
|
999
|
+
```typescript
|
|
1000
|
+
import djb2 from '@valkyriestudios/utils/hash/djb2';
|
|
1001
|
+
djb2('hello world');
|
|
1002
|
+
```
|
|
1003
|
+
|
|
981
1004
|
### hash/fnv1A(val:unknown)
|
|
982
|
-
Generate a fnv1A hash from an object
|
|
1005
|
+
Generate a fnv1A hash from an object/array/primitive/...
|
|
983
1006
|
```typescript
|
|
984
1007
|
import fnv1A from '@valkyriestudios/utils/hash/fnv1A';
|
|
985
|
-
fnv1A('hello world');
|
|
986
|
-
fnv1A({a:1,b:2}); // 361168128
|
|
987
|
-
fnv1A(4); // 1630425728
|
|
988
|
-
fnv1A(new RegExp(/ab+c/, 'i')); // 2131692544
|
|
989
|
-
fnv1A(new Date('2012-02-02')); // 1655579136
|
|
1008
|
+
fnv1A('hello world');
|
|
990
1009
|
```
|
|
991
1010
|
|
|
992
1011
|
### Is
|
package/array/dedupe.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
type
|
|
1
|
+
type DedupeOptionsBase<T> = {
|
|
2
2
|
/**
|
|
3
3
|
* Pass a custom filter function which will be run in O(n) while deduping is going on
|
|
4
4
|
*/
|
|
5
5
|
filter_fn?: (el: T) => boolean;
|
|
6
6
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
type DedupeOptionsWithKey<T extends Record<string, unknown>> = DedupeOptionsBase<T> & {
|
|
8
|
+
/**
|
|
9
|
+
* Deduplicate based on a single property key of T
|
|
10
|
+
*/
|
|
11
|
+
key: keyof T;
|
|
12
|
+
};
|
|
13
|
+
type DedupeOptionsNoKey<T> = DedupeOptionsBase<T>;
|
|
14
|
+
declare function dedupe<T extends Record<string, unknown>>(val: T[], opts: DedupeOptionsWithKey<T>): T[];
|
|
15
|
+
declare function dedupe<T>(val: T[], opts?: DedupeOptionsNoKey<T>): T[];
|
|
14
16
|
export { dedupe, dedupe as default };
|
package/array/is.d.ts
CHANGED
package/array/isNotEmpty.d.ts
CHANGED
package/caching/memoize.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Turn a function into a memoized function. An optional resolver function can be passed which allows custom cache key generation.
|
|
3
3
|
*
|
|
4
4
|
* Example:
|
|
5
|
-
* const memoized_function = memoize((a) =>
|
|
5
|
+
* const memoized_function = memoize((a) => djb2(a));
|
|
6
6
|
*
|
|
7
7
|
* @param fn - Function to memoize
|
|
8
8
|
* @param resolver - Optional resolver function to generate cache key. If not passed the first argument is used as map key
|
package/cjs/array/dedupe.js
CHANGED
|
@@ -2,53 +2,41 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.dedupe = dedupe;
|
|
4
4
|
exports.default = dedupe;
|
|
5
|
-
const
|
|
6
|
-
const REPL_TRUE = 'true';
|
|
7
|
-
const REPL_FALSE = 'false';
|
|
8
|
-
const REPL_UNDEF = 'undefined';
|
|
9
|
-
const REPL_NULL = 'null';
|
|
10
|
-
function getTypeString(el) {
|
|
11
|
-
switch (typeof el) {
|
|
12
|
-
case 'string':
|
|
13
|
-
return el;
|
|
14
|
-
case 'number':
|
|
15
|
-
return Number.isNaN(el) || !Number.isFinite(el) ? REPL_NAN : String(el);
|
|
16
|
-
case 'boolean':
|
|
17
|
-
return el ? REPL_TRUE : REPL_FALSE;
|
|
18
|
-
case 'undefined':
|
|
19
|
-
return REPL_UNDEF;
|
|
20
|
-
case 'object':
|
|
21
|
-
if (el === null) {
|
|
22
|
-
return REPL_NULL;
|
|
23
|
-
}
|
|
24
|
-
else if (Array.isArray(el) || el.toString() === '[object Object]') {
|
|
25
|
-
return JSON.stringify(el);
|
|
26
|
-
}
|
|
27
|
-
else if (el instanceof RegExp) {
|
|
28
|
-
return el.toString();
|
|
29
|
-
}
|
|
30
|
-
else if (el instanceof Date) {
|
|
31
|
-
return String(el.getTime());
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return '';
|
|
35
|
-
}
|
|
5
|
+
const utils_1 = require("../hash/utils");
|
|
36
6
|
function dedupe(val, opts) {
|
|
37
7
|
if (!Array.isArray(val))
|
|
38
8
|
return [];
|
|
39
9
|
const FILTER_FN = opts?.filter_fn;
|
|
10
|
+
const KEY = opts?.key;
|
|
40
11
|
const set = new Set();
|
|
41
12
|
const acc = [];
|
|
42
13
|
let hash;
|
|
43
14
|
const len = val.length;
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
15
|
+
if (KEY) {
|
|
16
|
+
const CUSTOM_FILTER_FN = typeof FILTER_FN === 'function'
|
|
17
|
+
? (el) => el && Object.prototype.toString.call(el) === '[object Object]' && FILTER_FN(el)
|
|
18
|
+
: (el) => el && Object.prototype.toString.call(el) === '[object Object]';
|
|
19
|
+
for (let i = 0; i < len; i++) {
|
|
20
|
+
const el = val[i];
|
|
21
|
+
if (!CUSTOM_FILTER_FN(el))
|
|
22
|
+
continue;
|
|
23
|
+
hash = (0, utils_1.toString)(el[KEY]);
|
|
24
|
+
if (!set.has(hash)) {
|
|
25
|
+
set.add(hash);
|
|
26
|
+
acc.push(el);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
for (let i = 0; i < len; i++) {
|
|
32
|
+
const el = val[i];
|
|
33
|
+
if (FILTER_FN && !FILTER_FN(el))
|
|
34
|
+
continue;
|
|
35
|
+
hash = (0, utils_1.toString)(el);
|
|
36
|
+
if (!set.has(hash)) {
|
|
37
|
+
set.add(hash);
|
|
38
|
+
acc.push(el);
|
|
39
|
+
}
|
|
52
40
|
}
|
|
53
41
|
}
|
|
54
42
|
return acc;
|
package/cjs/caching/memoize.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.memoize = memoize;
|
|
7
7
|
exports.default = memoize;
|
|
8
8
|
const isAsync_1 = __importDefault(require("../function/isAsync"));
|
|
9
|
-
const
|
|
9
|
+
const djb2_1 = __importDefault(require("../hash/djb2"));
|
|
10
10
|
const isIntegerAbove_1 = __importDefault(require("../number/isIntegerAbove"));
|
|
11
11
|
const LRU_1 = __importDefault(require("./LRU"));
|
|
12
12
|
function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100) {
|
|
@@ -16,7 +16,7 @@ function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100)
|
|
|
16
16
|
const memoized = (0, isAsync_1.default)(fn)
|
|
17
17
|
? async function (...args) {
|
|
18
18
|
let key = isResolverFn ? resolver(...args) : args[0];
|
|
19
|
-
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) :
|
|
19
|
+
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) : (0, djb2_1.default)(key);
|
|
20
20
|
const cached_val = cache.get(key);
|
|
21
21
|
const now = Date.now();
|
|
22
22
|
if (cached_val !== undefined && (cache_duration === false || (now - cached_val.ts) < cache_duration)) {
|
|
@@ -28,7 +28,7 @@ function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100)
|
|
|
28
28
|
}
|
|
29
29
|
: function (...args) {
|
|
30
30
|
let key = isResolverFn ? resolver(...args) : args[0];
|
|
31
|
-
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) :
|
|
31
|
+
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) : (0, djb2_1.default)(key);
|
|
32
32
|
const cached_val = cache.get(key);
|
|
33
33
|
const now = Date.now();
|
|
34
34
|
if (cached_val !== undefined && (cache_duration === false || (now - cached_val.ts) < cache_duration)) {
|
package/cjs/hash/djb2.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.djb2 = djb2;
|
|
4
|
+
exports.default = djb2;
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
function djb2(data) {
|
|
7
|
+
let hash = 5381;
|
|
8
|
+
const normalized = (0, utils_1.toString)(data);
|
|
9
|
+
const len = normalized.length;
|
|
10
|
+
for (let i = 0; i < len; i++) {
|
|
11
|
+
hash = ((hash << 5) + hash) ^ normalized.charCodeAt(i);
|
|
12
|
+
}
|
|
13
|
+
return String(hash >>> 0);
|
|
14
|
+
}
|
package/cjs/hash/fnv1A.js
CHANGED
|
@@ -3,52 +3,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.FNV_64 = exports.FNV_32 = void 0;
|
|
4
4
|
exports.fnv1A = fnv1A;
|
|
5
5
|
exports.default = fnv1A;
|
|
6
|
+
const utils_1 = require("./utils");
|
|
6
7
|
exports.FNV_32 = 2166136261;
|
|
7
|
-
exports.FNV_64 =
|
|
8
|
-
const REPL_NAN = 'nan';
|
|
9
|
-
const REPL_TRUE = 'true';
|
|
10
|
-
const REPL_FALSE = 'false';
|
|
11
|
-
const REPL_UNDEF = 'undefined';
|
|
12
|
-
const REPL_NULL = 'null';
|
|
8
|
+
exports.FNV_64 = 1099511628211n;
|
|
13
9
|
function fnv1A(data, offset = exports.FNV_32) {
|
|
14
10
|
let hash = offset;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
case 'string':
|
|
18
|
-
sanitized = data;
|
|
19
|
-
break;
|
|
20
|
-
case 'number':
|
|
21
|
-
sanitized = Number.isNaN(data) || !Number.isFinite(data) ? REPL_NAN : String(data);
|
|
22
|
-
break;
|
|
23
|
-
case 'boolean':
|
|
24
|
-
sanitized = data ? REPL_TRUE : REPL_FALSE;
|
|
25
|
-
break;
|
|
26
|
-
case 'undefined':
|
|
27
|
-
sanitized = REPL_UNDEF;
|
|
28
|
-
break;
|
|
29
|
-
case 'object':
|
|
30
|
-
if (data === null) {
|
|
31
|
-
sanitized = REPL_NULL;
|
|
32
|
-
}
|
|
33
|
-
else if (Array.isArray(data) || Object.prototype.toString.call(data) === '[object Object]') {
|
|
34
|
-
sanitized = JSON.stringify(data);
|
|
35
|
-
}
|
|
36
|
-
else if (data instanceof RegExp) {
|
|
37
|
-
sanitized = String(data);
|
|
38
|
-
}
|
|
39
|
-
else if (data instanceof Date) {
|
|
40
|
-
sanitized = String(data.getTime());
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
throw new TypeError('An FNV1A Hash could not be calculated for this datatype');
|
|
44
|
-
}
|
|
45
|
-
break;
|
|
46
|
-
default:
|
|
47
|
-
throw new TypeError('An FNV1A Hash could not be calculated for this datatype');
|
|
48
|
-
}
|
|
49
|
-
const len = sanitized.length;
|
|
11
|
+
const normalized = (0, utils_1.toString)(data);
|
|
12
|
+
const len = normalized.length;
|
|
50
13
|
for (let i = 0; i < len; i++) {
|
|
51
|
-
hash ^=
|
|
14
|
+
hash ^= normalized.charCodeAt(i);
|
|
52
15
|
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
53
16
|
}
|
|
54
17
|
return hash >>> 0;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toString = toString;
|
|
4
|
+
function toString(raw) {
|
|
5
|
+
switch (typeof raw) {
|
|
6
|
+
case 'string':
|
|
7
|
+
return raw;
|
|
8
|
+
case 'number':
|
|
9
|
+
return Number.isFinite(raw) ? String(raw) : 'nan';
|
|
10
|
+
case 'bigint':
|
|
11
|
+
return raw.toString();
|
|
12
|
+
case 'boolean':
|
|
13
|
+
return raw ? 'true' : 'false';
|
|
14
|
+
case 'undefined':
|
|
15
|
+
return 'undefined';
|
|
16
|
+
case 'object':
|
|
17
|
+
if (raw === null) {
|
|
18
|
+
return 'null';
|
|
19
|
+
}
|
|
20
|
+
else if (Array.isArray(raw) || Object.prototype.toString.call(raw) === '[object Object]') {
|
|
21
|
+
return JSON.stringify(raw);
|
|
22
|
+
}
|
|
23
|
+
else if (raw instanceof RegExp) {
|
|
24
|
+
return String(raw);
|
|
25
|
+
}
|
|
26
|
+
else if (raw instanceof Date) {
|
|
27
|
+
return String(raw.getTime());
|
|
28
|
+
}
|
|
29
|
+
else if (raw instanceof Error) {
|
|
30
|
+
return raw.name + '|' + raw.message;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new TypeError('A Hash could not be calculated for this datatype');
|
|
34
|
+
}
|
|
35
|
+
case 'symbol':
|
|
36
|
+
return String(raw);
|
|
37
|
+
default:
|
|
38
|
+
throw new TypeError('A Hash could not be calculated for this datatype');
|
|
39
|
+
}
|
|
40
|
+
}
|
package/esm/array/dedupe.js
CHANGED
|
@@ -1,50 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
const REPL_TRUE = 'true';
|
|
3
|
-
const REPL_FALSE = 'false';
|
|
4
|
-
const REPL_UNDEF = 'undefined';
|
|
5
|
-
const REPL_NULL = 'null';
|
|
6
|
-
function getTypeString(el) {
|
|
7
|
-
switch (typeof el) {
|
|
8
|
-
case 'string':
|
|
9
|
-
return el;
|
|
10
|
-
case 'number':
|
|
11
|
-
return Number.isNaN(el) || !Number.isFinite(el) ? REPL_NAN : String(el);
|
|
12
|
-
case 'boolean':
|
|
13
|
-
return el ? REPL_TRUE : REPL_FALSE;
|
|
14
|
-
case 'undefined':
|
|
15
|
-
return REPL_UNDEF;
|
|
16
|
-
case 'object':
|
|
17
|
-
if (el === null) {
|
|
18
|
-
return REPL_NULL;
|
|
19
|
-
}
|
|
20
|
-
else if (Array.isArray(el) || el.toString() === '[object Object]') {
|
|
21
|
-
return JSON.stringify(el);
|
|
22
|
-
}
|
|
23
|
-
else if (el instanceof RegExp) {
|
|
24
|
-
return el.toString();
|
|
25
|
-
}
|
|
26
|
-
else if (el instanceof Date) {
|
|
27
|
-
return String(el.getTime());
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return '';
|
|
31
|
-
}
|
|
1
|
+
import { toString } from '../hash/utils';
|
|
32
2
|
function dedupe(val, opts) {
|
|
33
3
|
if (!Array.isArray(val))
|
|
34
4
|
return [];
|
|
35
5
|
const FILTER_FN = opts?.filter_fn;
|
|
6
|
+
const KEY = opts?.key;
|
|
36
7
|
const set = new Set();
|
|
37
8
|
const acc = [];
|
|
38
9
|
let hash;
|
|
39
10
|
const len = val.length;
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
11
|
+
if (KEY) {
|
|
12
|
+
const CUSTOM_FILTER_FN = typeof FILTER_FN === 'function'
|
|
13
|
+
? (el) => el && Object.prototype.toString.call(el) === '[object Object]' && FILTER_FN(el)
|
|
14
|
+
: (el) => el && Object.prototype.toString.call(el) === '[object Object]';
|
|
15
|
+
for (let i = 0; i < len; i++) {
|
|
16
|
+
const el = val[i];
|
|
17
|
+
if (!CUSTOM_FILTER_FN(el))
|
|
18
|
+
continue;
|
|
19
|
+
hash = toString(el[KEY]);
|
|
20
|
+
if (!set.has(hash)) {
|
|
21
|
+
set.add(hash);
|
|
22
|
+
acc.push(el);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (let i = 0; i < len; i++) {
|
|
28
|
+
const el = val[i];
|
|
29
|
+
if (FILTER_FN && !FILTER_FN(el))
|
|
30
|
+
continue;
|
|
31
|
+
hash = toString(el);
|
|
32
|
+
if (!set.has(hash)) {
|
|
33
|
+
set.add(hash);
|
|
34
|
+
acc.push(el);
|
|
35
|
+
}
|
|
48
36
|
}
|
|
49
37
|
}
|
|
50
38
|
return acc;
|
package/esm/caching/memoize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import isAsyncFunction from '../function/isAsync';
|
|
2
|
-
import
|
|
2
|
+
import djb2 from '../hash/djb2';
|
|
3
3
|
import isIntegerGt from '../number/isIntegerAbove';
|
|
4
4
|
import LRU from './LRU';
|
|
5
5
|
function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100) {
|
|
@@ -9,7 +9,7 @@ function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100)
|
|
|
9
9
|
const memoized = isAsyncFunction(fn)
|
|
10
10
|
? async function (...args) {
|
|
11
11
|
let key = isResolverFn ? resolver(...args) : args[0];
|
|
12
|
-
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) :
|
|
12
|
+
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) : djb2(key);
|
|
13
13
|
const cached_val = cache.get(key);
|
|
14
14
|
const now = Date.now();
|
|
15
15
|
if (cached_val !== undefined && (cache_duration === false || (now - cached_val.ts) < cache_duration)) {
|
|
@@ -21,7 +21,7 @@ function memoize(fn, resolver, cache_duration_ms = false, cache_max_size = 100)
|
|
|
21
21
|
}
|
|
22
22
|
: function (...args) {
|
|
23
23
|
let key = isResolverFn ? resolver(...args) : args[0];
|
|
24
|
-
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) :
|
|
24
|
+
key = typeof key === 'string' ? key : Number.isFinite(key) ? String(key) : djb2(key);
|
|
25
25
|
const cached_val = cache.get(key);
|
|
26
26
|
const now = Date.now();
|
|
27
27
|
if (cached_val !== undefined && (cache_duration === false || (now - cached_val.ts) < cache_duration)) {
|
package/esm/hash/djb2.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { toString } from './utils';
|
|
2
|
+
function djb2(data) {
|
|
3
|
+
let hash = 5381;
|
|
4
|
+
const normalized = toString(data);
|
|
5
|
+
const len = normalized.length;
|
|
6
|
+
for (let i = 0; i < len; i++) {
|
|
7
|
+
hash = ((hash << 5) + hash) ^ normalized.charCodeAt(i);
|
|
8
|
+
}
|
|
9
|
+
return String(hash >>> 0);
|
|
10
|
+
}
|
|
11
|
+
export { djb2, djb2 as default };
|
package/esm/hash/fnv1A.js
CHANGED
|
@@ -1,49 +1,12 @@
|
|
|
1
|
+
import { toString } from './utils';
|
|
1
2
|
export const FNV_32 = 2166136261;
|
|
2
|
-
export const FNV_64 =
|
|
3
|
-
const REPL_NAN = 'nan';
|
|
4
|
-
const REPL_TRUE = 'true';
|
|
5
|
-
const REPL_FALSE = 'false';
|
|
6
|
-
const REPL_UNDEF = 'undefined';
|
|
7
|
-
const REPL_NULL = 'null';
|
|
3
|
+
export const FNV_64 = 1099511628211n;
|
|
8
4
|
function fnv1A(data, offset = FNV_32) {
|
|
9
5
|
let hash = offset;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
case 'string':
|
|
13
|
-
sanitized = data;
|
|
14
|
-
break;
|
|
15
|
-
case 'number':
|
|
16
|
-
sanitized = Number.isNaN(data) || !Number.isFinite(data) ? REPL_NAN : String(data);
|
|
17
|
-
break;
|
|
18
|
-
case 'boolean':
|
|
19
|
-
sanitized = data ? REPL_TRUE : REPL_FALSE;
|
|
20
|
-
break;
|
|
21
|
-
case 'undefined':
|
|
22
|
-
sanitized = REPL_UNDEF;
|
|
23
|
-
break;
|
|
24
|
-
case 'object':
|
|
25
|
-
if (data === null) {
|
|
26
|
-
sanitized = REPL_NULL;
|
|
27
|
-
}
|
|
28
|
-
else if (Array.isArray(data) || Object.prototype.toString.call(data) === '[object Object]') {
|
|
29
|
-
sanitized = JSON.stringify(data);
|
|
30
|
-
}
|
|
31
|
-
else if (data instanceof RegExp) {
|
|
32
|
-
sanitized = String(data);
|
|
33
|
-
}
|
|
34
|
-
else if (data instanceof Date) {
|
|
35
|
-
sanitized = String(data.getTime());
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
throw new TypeError('An FNV1A Hash could not be calculated for this datatype');
|
|
39
|
-
}
|
|
40
|
-
break;
|
|
41
|
-
default:
|
|
42
|
-
throw new TypeError('An FNV1A Hash could not be calculated for this datatype');
|
|
43
|
-
}
|
|
44
|
-
const len = sanitized.length;
|
|
6
|
+
const normalized = toString(data);
|
|
7
|
+
const len = normalized.length;
|
|
45
8
|
for (let i = 0; i < len; i++) {
|
|
46
|
-
hash ^=
|
|
9
|
+
hash ^= normalized.charCodeAt(i);
|
|
47
10
|
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
48
11
|
}
|
|
49
12
|
return hash >>> 0;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function toString(raw) {
|
|
2
|
+
switch (typeof raw) {
|
|
3
|
+
case 'string':
|
|
4
|
+
return raw;
|
|
5
|
+
case 'number':
|
|
6
|
+
return Number.isFinite(raw) ? String(raw) : 'nan';
|
|
7
|
+
case 'bigint':
|
|
8
|
+
return raw.toString();
|
|
9
|
+
case 'boolean':
|
|
10
|
+
return raw ? 'true' : 'false';
|
|
11
|
+
case 'undefined':
|
|
12
|
+
return 'undefined';
|
|
13
|
+
case 'object':
|
|
14
|
+
if (raw === null) {
|
|
15
|
+
return 'null';
|
|
16
|
+
}
|
|
17
|
+
else if (Array.isArray(raw) || Object.prototype.toString.call(raw) === '[object Object]') {
|
|
18
|
+
return JSON.stringify(raw);
|
|
19
|
+
}
|
|
20
|
+
else if (raw instanceof RegExp) {
|
|
21
|
+
return String(raw);
|
|
22
|
+
}
|
|
23
|
+
else if (raw instanceof Date) {
|
|
24
|
+
return String(raw.getTime());
|
|
25
|
+
}
|
|
26
|
+
else if (raw instanceof Error) {
|
|
27
|
+
return raw.name + '|' + raw.message;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
throw new TypeError('A Hash could not be calculated for this datatype');
|
|
31
|
+
}
|
|
32
|
+
case 'symbol':
|
|
33
|
+
return String(raw);
|
|
34
|
+
default:
|
|
35
|
+
throw new TypeError('A Hash could not be calculated for this datatype');
|
|
36
|
+
}
|
|
37
|
+
}
|
package/hash/djb2.d.ts
ADDED
package/hash/fnv1A.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const FNV_32 = 2166136261;
|
|
2
|
-
export declare const FNV_64 =
|
|
2
|
+
export declare const FNV_64 = 1099511628211n;
|
|
3
3
|
/**
|
|
4
4
|
* Convert a provided value into a Fowler-Noll-Vo 1A hash
|
|
5
5
|
* For more info: https://tools.ietf.org/html/draft-eastlake-fnv-03
|
package/hash/utils.d.ts
ADDED
package/index.d.ts
CHANGED
|
@@ -2,11 +2,19 @@ declare module "equal" {
|
|
|
2
2
|
function equal(a: any, b: any): boolean;
|
|
3
3
|
export { equal, equal as default };
|
|
4
4
|
}
|
|
5
|
+
declare module "hash/utils" {
|
|
6
|
+
export function toString(raw: unknown): string;
|
|
7
|
+
}
|
|
5
8
|
declare module "array/dedupe" {
|
|
6
|
-
type
|
|
9
|
+
type DedupeOptionsBase<T> = {
|
|
7
10
|
filter_fn?: (el: T) => boolean;
|
|
8
11
|
};
|
|
9
|
-
|
|
12
|
+
type DedupeOptionsWithKey<T extends Record<string, unknown>> = DedupeOptionsBase<T> & {
|
|
13
|
+
key: keyof T;
|
|
14
|
+
};
|
|
15
|
+
type DedupeOptionsNoKey<T> = DedupeOptionsBase<T>;
|
|
16
|
+
function dedupe<T extends Record<string, unknown>>(val: T[], opts: DedupeOptionsWithKey<T>): T[];
|
|
17
|
+
function dedupe<T>(val: T[], opts?: DedupeOptionsNoKey<T>): T[];
|
|
10
18
|
export { dedupe, dedupe as default };
|
|
11
19
|
}
|
|
12
20
|
declare module "number/round" {
|
|
@@ -91,7 +99,7 @@ declare module "array/mapPrimitive" {
|
|
|
91
99
|
export { mapPrimitive, mapPrimitive as default };
|
|
92
100
|
}
|
|
93
101
|
declare module "object/isNotEmpty" {
|
|
94
|
-
function isNotEmptyObject<T extends Record<string,
|
|
102
|
+
function isNotEmptyObject<T extends Record<string, unknown> = Record<string, unknown>>(val: T | unknown): val is T;
|
|
95
103
|
export { isNotEmptyObject, isNotEmptyObject as default };
|
|
96
104
|
}
|
|
97
105
|
declare module "array/groupBy" {
|
|
@@ -111,7 +119,7 @@ declare module "array/split" {
|
|
|
111
119
|
export { split, split as default };
|
|
112
120
|
}
|
|
113
121
|
declare module "object/is" {
|
|
114
|
-
function isObject<T extends Record<string,
|
|
122
|
+
function isObject<T extends Record<string, unknown> = Record<string, unknown>>(val: T | unknown): val is T;
|
|
115
123
|
export { isObject, isObject as default };
|
|
116
124
|
}
|
|
117
125
|
declare module "array/sort" {
|
|
@@ -127,11 +135,11 @@ declare module "array/sort" {
|
|
|
127
135
|
export { sort, sort as default };
|
|
128
136
|
}
|
|
129
137
|
declare module "array/is" {
|
|
130
|
-
function isArray(val: unknown): val is
|
|
138
|
+
function isArray<T = unknown>(val: unknown): val is T[];
|
|
131
139
|
export { isArray, isArray as default };
|
|
132
140
|
}
|
|
133
141
|
declare module "array/isNotEmpty" {
|
|
134
|
-
function isNotEmptyArray(val: unknown): val is
|
|
142
|
+
function isNotEmptyArray<T = unknown>(val: unknown): val is T[];
|
|
135
143
|
export { isNotEmptyArray, isNotEmptyArray as default };
|
|
136
144
|
}
|
|
137
145
|
declare module "array/index" {
|
|
@@ -635,11 +643,9 @@ declare module "is" {
|
|
|
635
643
|
}>;
|
|
636
644
|
export { Is, Is as default };
|
|
637
645
|
}
|
|
638
|
-
declare module "hash/
|
|
639
|
-
|
|
640
|
-
export
|
|
641
|
-
function fnv1A(data: unknown, offset?: number): number;
|
|
642
|
-
export { fnv1A, fnv1A as default };
|
|
646
|
+
declare module "hash/djb2" {
|
|
647
|
+
function djb2(data: unknown): string;
|
|
648
|
+
export { djb2, djb2 as default };
|
|
643
649
|
}
|
|
644
650
|
declare module "caching/memoize" {
|
|
645
651
|
function memoize<T extends (...args: any[]) => unknown>(fn: T, resolver?: (...args: Parameters<T>) => any, cache_duration_ms?: number | false, cache_max_size?: number): T;
|
|
@@ -692,6 +698,12 @@ declare module "deep/index" {
|
|
|
692
698
|
export { deepSet as set } from "deep/set";
|
|
693
699
|
export { deepSet } from "deep/set";
|
|
694
700
|
}
|
|
701
|
+
declare module "hash/fnv1A" {
|
|
702
|
+
export const FNV_32 = 2166136261;
|
|
703
|
+
export const FNV_64 = 1099511628211n;
|
|
704
|
+
function fnv1A(data: unknown, offset?: number): number;
|
|
705
|
+
export { fnv1A, fnv1A as default };
|
|
706
|
+
}
|
|
695
707
|
declare module "hash/guid" {
|
|
696
708
|
function guid(): string;
|
|
697
709
|
export { guid, guid as default };
|
package/object/is.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @param {unknown} val - Value to verify
|
|
5
5
|
*/
|
|
6
|
-
declare function isObject<T extends Record<string,
|
|
6
|
+
declare function isObject<T extends Record<string, unknown> = Record<string, unknown>>(val: T | unknown): val is T;
|
|
7
7
|
export { isObject, isObject as default };
|
package/object/isNotEmpty.d.ts
CHANGED
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @param {unknown} val - Value to verify
|
|
5
5
|
*/
|
|
6
|
-
declare function isNotEmptyObject<T extends Record<string,
|
|
6
|
+
declare function isNotEmptyObject<T extends Record<string, unknown> = Record<string, unknown>>(val: T | unknown): val is T;
|
|
7
7
|
export { isNotEmptyObject, isNotEmptyObject as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valkyriestudios/utils",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.43.0",
|
|
4
4
|
"description": "A collection of single-function utilities for common tasks",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Peter Vermeulen",
|
|
@@ -514,12 +514,12 @@
|
|
|
514
514
|
}
|
|
515
515
|
},
|
|
516
516
|
"devDependencies": {
|
|
517
|
-
"@types/node": "^22.
|
|
518
|
-
"@vitest/coverage-v8": "^3.2.
|
|
517
|
+
"@types/node": "^22.18.0",
|
|
518
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
519
519
|
"esbuild-register": "^3.6.0",
|
|
520
|
-
"eslint": "^9.
|
|
521
|
-
"typescript": "^5.
|
|
522
|
-
"typescript-eslint": "^8.
|
|
523
|
-
"vitest": "^3.2.
|
|
520
|
+
"eslint": "^9.34.0",
|
|
521
|
+
"typescript": "^5.9.2",
|
|
522
|
+
"typescript-eslint": "^8.41.0",
|
|
523
|
+
"vitest": "^3.2.4"
|
|
524
524
|
}
|
|
525
525
|
}
|