@saulx/utils
Advanced tools
Comparing version 1.0.6 to 1.0.7
@@ -6,2 +6,3 @@ export declare const stringHash: (str: string, hash?: number) => number; | ||
export declare const hashObjectIgnoreKeyOrder: (props: object) => number; | ||
export declare const hash: (val: any) => number; | ||
export declare const hash: (val: any, size?: number) => number; | ||
export declare const hashCompact: (val: any, size?: number) => string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.hash = exports.hashObjectIgnoreKeyOrder = exports.hashObject = exports.hashObjectNest = exports.hashObjectIgnoreKeyOrderNest = exports.stringHash = void 0; | ||
exports.hashCompact = exports.hash = exports.hashObjectIgnoreKeyOrder = exports.hashObject = exports.hashObjectNest = exports.hashObjectIgnoreKeyOrderNest = exports.stringHash = void 0; | ||
exports.stringHash = (str, hash = 5381) => { | ||
@@ -135,20 +135,100 @@ var i = str.length; | ||
}; | ||
exports.hash = (val) => { | ||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; | ||
const toString = (hash) => { | ||
let result = ''; | ||
let mod; | ||
do { | ||
mod = hash % 62; | ||
result = chars.charAt(mod) + result; | ||
hash = Math.floor(hash / 62); | ||
} while (hash > 0); | ||
return result; | ||
}; | ||
// want bits probably | ||
exports.hash = (val, size) => { | ||
let result; | ||
if (typeof val === 'object') { | ||
if (val === null) { | ||
return 0; | ||
result = 0; | ||
} | ||
else { | ||
return exports.hashObject(val) >>> 0; | ||
result = exports.hashObject(val) >>> 0; | ||
} | ||
} | ||
else { | ||
if (typeof val === 'number') { | ||
return (nullHash ^ val) >>> 0; | ||
if (typeof val === 'boolean') { | ||
result = hashBool(val) >>> 0; | ||
} | ||
else if (typeof val === 'number') { | ||
result = ((nullHash ^ val) * size) >>> 0; | ||
} | ||
else { | ||
return exports.stringHash(val) >>> 0; | ||
result = exports.stringHash(val) >>> 0; | ||
} | ||
} | ||
if (size) { | ||
if (size < 10) { | ||
throw new Error('Minimum size for 32 bits is 10 numbers'); | ||
} | ||
const len = Math.ceil(Math.log10(result + 1)); | ||
if (len < size) { | ||
return result * Math.pow(10, size - len); | ||
} | ||
} | ||
return result; | ||
}; | ||
exports.hashCompact = (val, size) => { | ||
let result; | ||
if (typeof val === 'object') { | ||
if (val === null) { | ||
result = 0; | ||
} | ||
else { | ||
if (size && size > 9 && val.constructor === Array) { | ||
let str = ''; | ||
const arraySize = val.length; | ||
for (let i = 0; i < arraySize; i++) { | ||
str += toString(exports.hash(val[i])); | ||
} | ||
const len = str.length; | ||
if (len < size) { | ||
str += 'x'; | ||
if (len + 1 < size) { | ||
str += new Array(size - len).join('0'); | ||
} | ||
} | ||
else if (len > size) { | ||
return str.slice(0, size); | ||
} | ||
return str; | ||
} | ||
else { | ||
result = exports.hashObject(val) >>> 0; | ||
} | ||
} | ||
} | ||
else { | ||
if (typeof val === 'boolean') { | ||
result = hashBool(val) >>> 0; | ||
} | ||
else if (typeof val === 'number') { | ||
result = ((nullHash ^ val) * size) >>> 0; | ||
} | ||
else { | ||
result = exports.stringHash(val) >>> 0; | ||
} | ||
} | ||
if (size < 6) { | ||
throw new Error('Minimum size for 32 bits is 6 characters'); | ||
} | ||
let x = toString(result); | ||
const len = x.length; | ||
if (len < size) { | ||
x += 'x'; | ||
if (len + 1 < size) { | ||
x += new Array(size - len).join('0'); | ||
} | ||
} | ||
return x; | ||
}; | ||
//# sourceMappingURL=hash.js.map |
import deepCopy from './deepCopy'; | ||
export * from './hash'; | ||
export { deepCopy }; | ||
export * from './deepMerge'; | ||
import isObject from './isObject'; | ||
import wait from './wait'; | ||
import deepEqual from './deepEqual'; | ||
export { deepCopy, isObject, wait, deepEqual }; |
@@ -16,6 +16,13 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.deepCopy = void 0; | ||
exports.deepEqual = exports.wait = exports.isObject = exports.deepCopy = void 0; | ||
const deepCopy_1 = __importDefault(require("./deepCopy")); | ||
exports.deepCopy = deepCopy_1.default; | ||
__exportStar(require("./hash"), exports); | ||
__exportStar(require("./deepMerge"), exports); | ||
const isObject_1 = __importDefault(require("./isObject")); | ||
exports.isObject = isObject_1.default; | ||
const wait_1 = __importDefault(require("./wait")); | ||
exports.wait = wait_1.default; | ||
const deepEqual_1 = __importDefault(require("./deepEqual")); | ||
exports.deepEqual = deepEqual_1.default; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@saulx/utils", | ||
"main": "./dist/index.js", | ||
"version": "1.0.6", | ||
"version": "1.0.7", | ||
"scripts": { | ||
@@ -6,0 +6,0 @@ "build": "tsc", |
146
README.md
# utils | ||
Saulx utils package | ||
Saulx utils package, hashes are non cryptographic 32 bit hashes | ||
## hash | ||
Create a hash for any data type | ||
```javascript | ||
@@ -11,4 +15,144 @@ import { hash } from '@saulx/utils' | ||
```javascript | ||
import { hash } from '@saulx/utils' | ||
// pads extra zeroes | ||
console.log(hash({ x: true }, 15)) | ||
``` | ||
## hashCompact | ||
Create a hash for any data type, returns a base 62 string | ||
```javascript | ||
import { hashCompact } from '@saulx/utils' | ||
console.log(hashCompact({ x: true })) // -> CCoj0h | ||
``` | ||
Passing an array and specifying more chars makes a hash that uses all avaible space to make it more unique (becomes more then 32 bits) | ||
```javascript | ||
import { hashCompact } from '@saulx/utils' | ||
console.log(hashCompact([{ x: true }, 'bla', 'blurp', 'snurf'], 20)) // -> CCoj0hNFgt8MovDmLkmh | ||
``` | ||
## hashObject | ||
Create a hash from objects or arrays | ||
```javascript | ||
import { hashObject } from '@saulx/utils' | ||
console.log(hashObject({ x: true })) | ||
``` | ||
## hashObjectIgnoreKeyOrder | ||
Only works for objects, creates the same hash independent from object key order | ||
```javascript | ||
import { hashObject } from '@saulx/utils' | ||
console.log( | ||
hashObjectIgnoreKeyOrder({ x: true, y: true }) === | ||
hashObjectIgnoreKeyOrder({ y: true, x: true }) | ||
) | ||
``` | ||
## stringHash | ||
Creates a hash for strings | ||
```javascript | ||
import { stringHash } from '@saulx/utils' | ||
console.log(stringHash('bla bla bla')) | ||
``` | ||
## deepEqual | ||
Compare objects | ||
```javascript | ||
import { stringHash } from '@saulx/utils' | ||
console.log(deepEqual({ a: { b: true } }, { a: { b: true } })) // true | ||
``` | ||
## deepCopy | ||
Create a deepcopy of objects | ||
```javascript | ||
import { deepCopy } from '@saulx/utils' | ||
console.log(deepCopy({ x: true })) | ||
``` | ||
## deepMerge | ||
Merge an object into another object, arrays are treated as new values | ||
```javascript | ||
import { deepMerge } from '@saulx/utils' | ||
const a = { x: { a: { c: true, x: [1, 2] } } } | ||
const b = { y: true } | ||
const c = { x: { a: { b: true, x: ['bla'] } } } | ||
console.log(deepMerge(a, b)) | ||
console.log(deepMerge(a, b, c)) | ||
/* | ||
Logs | ||
{ | ||
x: { a: { b: true, c: true, x: ['bla']}}, | ||
y: true | ||
} | ||
*/ | ||
``` | ||
## deepMergeArrays | ||
Merge an object into another object, arrays are treated as objects | ||
```javascript | ||
import { deepMergeArrays } from '@saulx/utils' | ||
const a = { x: { a: { c: true, x: [1, 2, 3] } } } | ||
const b = { y: true } | ||
const c = { x: { a: { b: true, x: ['bla'] } } } | ||
console.log(deepMergeArrays(a, b)) | ||
console.log(deepMergeArrays(a, b, c)) | ||
/* | ||
Logs | ||
{ | ||
x: { a: { b: true, c: true, x: ['bla', 2, 3]}}, | ||
y: true | ||
} | ||
*/ | ||
``` | ||
## isObject | ||
Checks if a variable is an object and not an array | ||
```javascript | ||
import { isObject } from '@saulx/utils' | ||
console.log(isObject({ x: true })) // true | ||
console.log(isObject([1, 2, 3])) // false | ||
``` | ||
## wait | ||
Timeout in a promise, default is 100ms | ||
```javascript | ||
import { wait } from '@saulx/utils' | ||
const somethingAsync = async () => { | ||
await wait() // 100ms | ||
console.log('after 100ms') | ||
await wait(1000) | ||
console.log('after 1100ms') | ||
} | ||
somethingAsync() | ||
``` |
@@ -127,16 +127,94 @@ export const stringHash = (str: string, hash: number = 5381): number => { | ||
export const hash = (val: any): number => { | ||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | ||
const toString = (hash: number): string => { | ||
let result: string = '' | ||
let mod: number | ||
do { | ||
mod = hash % 62 | ||
result = chars.charAt(mod) + result | ||
hash = Math.floor(hash / 62) | ||
} while (hash > 0) | ||
return result | ||
} | ||
// want bits probably | ||
export const hash = (val: any, size?: number): number => { | ||
let result: number | ||
if (typeof val === 'object') { | ||
if (val === null) { | ||
return 0 | ||
result = 0 | ||
} else { | ||
return hashObject(val) >>> 0 | ||
result = hashObject(val) >>> 0 | ||
} | ||
} else { | ||
if (typeof val === 'number') { | ||
return (nullHash ^ val) >>> 0 | ||
if (typeof val === 'boolean') { | ||
result = hashBool(val) >>> 0 | ||
} else if (typeof val === 'number') { | ||
result = ((nullHash ^ val) * size) >>> 0 | ||
} else { | ||
return stringHash(val) >>> 0 | ||
result = stringHash(val) >>> 0 | ||
} | ||
} | ||
if (size) { | ||
if (size < 10) { | ||
throw new Error('Minimum size for 32 bits is 10 numbers') | ||
} | ||
const len = Math.ceil(Math.log10(result + 1)) | ||
if (len < size) { | ||
return result * Math.pow(10, size - len) | ||
} | ||
} | ||
return result | ||
} | ||
export const hashCompact = (val: any, size?: number): string => { | ||
let result: number | ||
if (typeof val === 'object') { | ||
if (val === null) { | ||
result = 0 | ||
} else { | ||
if (size && size > 9 && val.constructor === Array) { | ||
let str = '' | ||
const arraySize = val.length | ||
for (let i = 0; i < arraySize; i++) { | ||
str += toString(hash(val[i])) | ||
} | ||
const len = str.length | ||
if (len < size) { | ||
str += 'x' | ||
if (len + 1 < size) { | ||
str += new Array(size - len).join('0') | ||
} | ||
} else if (len > size) { | ||
return str.slice(0, size) | ||
} | ||
return str | ||
} else { | ||
result = hashObject(val) >>> 0 | ||
} | ||
} | ||
} else { | ||
if (typeof val === 'boolean') { | ||
result = hashBool(val) >>> 0 | ||
} else if (typeof val === 'number') { | ||
result = ((nullHash ^ val) * size) >>> 0 | ||
} else { | ||
result = stringHash(val) >>> 0 | ||
} | ||
} | ||
if (size < 6) { | ||
throw new Error('Minimum size for 32 bits is 6 characters') | ||
} | ||
let x = toString(result) | ||
const len = x.length | ||
if (len < size) { | ||
x += 'x' | ||
if (len + 1 < size) { | ||
x += new Array(size - len).join('0') | ||
} | ||
} | ||
return x | ||
} |
@@ -5,2 +5,10 @@ import deepCopy from './deepCopy' | ||
export { deepCopy } | ||
export * from './deepMerge' | ||
import isObject from './isObject' | ||
import wait from './wait' | ||
import deepEqual from './deepEqual' | ||
export { deepCopy, isObject, wait, deepEqual } |
import test from 'ava' | ||
import { hash, deepCopy, hashObject, hashObjectIgnoreKeyOrder } from '../src' | ||
import { | ||
hash, | ||
hashCompact, | ||
deepCopy, | ||
hashObject, | ||
hashObjectIgnoreKeyOrder, | ||
deepMerge, | ||
deepMergeArrays, | ||
wait, | ||
deepEqual | ||
} from '../src' | ||
@@ -33,3 +43,3 @@ test('hash', async t => { | ||
const x = hashObject(a) | ||
console.log(Date.now() - d, 'ms') | ||
console.log(' 1mil keys object takes', Date.now() - d, 'ms to hash') | ||
@@ -69,4 +79,9 @@ t.true(typeof x === 'number') | ||
const x = hashObjectIgnoreKeyOrder(a) | ||
console.log(Date.now() - d, 'ms') | ||
console.log( | ||
' 1mil keys object takes', | ||
Date.now() - d, | ||
'ms to hash ignore key order' | ||
) | ||
t.true(typeof x === 'number') | ||
@@ -344,1 +359,143 @@ }) | ||
}) | ||
test('deepMerge', async t => { | ||
const a = { | ||
b: { | ||
a: 'a!', | ||
c: [ | ||
{ x: true, y: false }, | ||
{ x: false, y: true } | ||
], | ||
d: { x: {} } | ||
} | ||
} | ||
const b = { | ||
b: { | ||
b: 'its b!', | ||
c: [{ x: true, y: true }], | ||
d: { x: { flap: true } } | ||
} | ||
} | ||
const r = deepCopy(a) | ||
deepMergeArrays(r, deepCopy(b)) | ||
t.deepEqual( | ||
r, | ||
{ | ||
b: { | ||
a: 'a!', | ||
c: [ | ||
{ x: true, y: true }, | ||
{ x: false, y: true } | ||
], | ||
d: { x: { flap: true } }, | ||
b: 'its b!' | ||
} | ||
}, | ||
'deep merge include arrays' | ||
) | ||
const r2 = deepCopy(a) | ||
deepMerge(r2, deepCopy(b)) | ||
t.deepEqual( | ||
r2, | ||
{ | ||
b: { | ||
a: 'a!', | ||
c: [{ x: true, y: true }], | ||
d: { x: { flap: true } }, | ||
b: 'its b!' | ||
} | ||
}, | ||
'deep merge exclude arrays' | ||
) | ||
const r3 = deepCopy(a) | ||
deepMerge( | ||
r3, | ||
{ | ||
b: { a: 'ja' } | ||
}, | ||
{ | ||
b: { x: 'snurf' } | ||
}, | ||
{ | ||
blarf: true | ||
} | ||
) | ||
t.deepEqual( | ||
r3, | ||
{ | ||
b: { | ||
a: 'ja', | ||
c: [ | ||
{ x: true, y: false }, | ||
{ x: false, y: true } | ||
], | ||
d: { x: {} }, | ||
x: 'snurf' | ||
}, | ||
blarf: true | ||
}, | ||
'multiple arguments' | ||
) | ||
}) | ||
test('wait ', async t => { | ||
var d = Date.now() | ||
await wait(1e3) | ||
t.true(Date.now() - d > 999) | ||
}) | ||
test('hash fixed length', async t => { | ||
const texts = [] | ||
for (let i = 0; i < 10000; i++) { | ||
const nr = Math.random() * 100 | ||
texts[i] = | ||
nr < 33 | ||
? { | ||
blxxxa: ~~(Math.random() * 100 * 10000).toString(16), | ||
bla: ~~(Math.random() * 100 * 10000).toString(16) | ||
} | ||
: nr < 66 | ||
? (Math.random() * 100 * 10000).toString(16) | ||
: (Math.random() * 100000 * 10000).toString(16) | ||
} | ||
for (let i = 0; i < 10; i++) { | ||
const bla = texts[i] | ||
const x = hash(bla, 15) | ||
const y = hashCompact(bla, 9) | ||
const a = hashCompact( | ||
['x', 'bla bla', 'snurkypatbs do it', { lifestyle: true }], | ||
10 | ||
) | ||
const z = hashCompact(bla, 6) | ||
const blap = hashCompact( | ||
['x', 'bla bla', 'snurkypatbs do it', { lifestyle: true }], | ||
20 | ||
) | ||
const blurp = hashCompact(['x', 'bla bla', 'snurkypatbs do it'], 10) | ||
t.is(x.toString().length, 15) | ||
t.is(y.length, 9) | ||
t.is(a.length, 10) | ||
t.is(z.length, 6) | ||
t.is(blap.length, 20) | ||
t.is(blurp.length, 10) | ||
} | ||
}) | ||
test('deepEqual ', async t => { | ||
const bla = { x: true, y: true, z: [1, 2, 3, 4, { x: true }] } | ||
const blarf = { x: true, y: true, z: [1, 2, 3, 4, { x: true }] } | ||
t.true(deepEqual(bla, blarf)) | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
56894
33
1375
158