diff --git a/packages/object-util/.eslintrc.js b/packages/object-util/.eslintrc.js new file mode 100644 index 0000000..7a0472e --- /dev/null +++ b/packages/object-util/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + "parserOptions": { + "project": __dirname + "/tsconfig.json" + } +} \ No newline at end of file diff --git a/packages/object-util/README.md b/packages/object-util/README.md new file mode 100644 index 0000000..80cdd23 --- /dev/null +++ b/packages/object-util/README.md @@ -0,0 +1,65 @@ +# URL Manager + +simple to use yet powerful Urls parser and formatter + +## Usage + +- Import URLManager + +```typescript +import ObjectUtil from '@dzeio/object-util' +// or +const ObjectUtil = require('@dzeio/object-util').default + +// or you can import each funcitons individually +``` + +- or import it from the browser + +```html + + +``` + +- Create a new instance + +```typescript +// Create a new instance +const url = new URLManager() // you can have an URL, URLSearchParams Objects or a string as parameter +// or +const url = URLManager.fromLocation() // Browser only return a new instance from the current location +``` + +- explore ! + +```typescript + +// Does the same as Array.map +objectMap(object, (value, key) => {value + "pouet"}) + +// does the same as Array.forEach with the addon of stopping if false is returned (like break) +// and return if loop was finished or not +objectLoop(object, (value, key) => {}) + +// return the values of an object as an array +objectToArray(object) + +// return the keys of an object as an array +ObjectKeys(object) + +// return the count of an object keys +objectSize(object) + +// like Array.sort it sort and return an ordered object +objectSort(object, /*optionnal*/ (key1, key2) => key1 - key2) + +// deeply clone an object +cloneObject(object) + +// deeply set an object value while creating empty childs if necessary +//ex: this will return {path1, [{path3: 'value'}]} if object is an empty object +objectSet(object, ['path1', 0, 'path3'], 'value') + +// deeply compare two objects +objectEqual(object, object2) +``` diff --git a/packages/object-util/__tests__/index.test.ts b/packages/object-util/__tests__/index.test.ts index ee8ccdf..c3d7010 100644 --- a/packages/object-util/__tests__/index.test.ts +++ b/packages/object-util/__tests__/index.test.ts @@ -1,9 +1,58 @@ /// -import { objectSize, objectToArray, objectMap, objectSort, cloneObject, objectEqual } from '../src/ObjectUtil' +import { objectSize, objectToArray, objectMap, objectSort, cloneObject, objectEqual, objectKeys, objectSet, objectLoop } from '../src/ObjectUtil' +describe('Object Map tests', () => { + it('should works', () => { + const obj = { + pouet: 'first', + toto: 'second' + } + expect(objectMap(obj, (value, index) => { + return [index, value] + })).toEqual([['pouet', 'first'],['toto','second']]) + }) +}) -describe('Basic tests', () => { +describe('Object Loop Tests', () => { + it('Should works', () => { + const obj = { + pouet: true, + toto: 'object-util' + } + objectLoop(obj, (value, key) => { + if (key === 'pouet') { + expect(value).toBe(true) + } else if (key === 'toto') { + expect(value).toBe('object-util') + } else { + throw "it should not come here" + } + }) + }) +}) + +describe('Object To Array Tests', () => { + it('Should Works', () => { + const obj = { + pouet: 'first', + toto: 'second' + } + expect(objectToArray(obj)).toEqual(['first', 'second']) + }) +}) + +describe('Object Keys Tests', () => { + it('Should Works', () => { + const obj = { + pouet: 'first', + toto: 'second' + } + expect(objectKeys(obj)).toEqual(['pouet', 'toto']) + }) +}) + +describe('Object Sort Tests', () => { it('shoud return length of the object', () => { const obj = { index0: true, @@ -20,25 +69,9 @@ describe('Basic tests', () => { } expect(objectSize(obj)).toBe(11) }) +}) - it('should convert the object to an array', () => { - const obj = { - pouet: 'first', - toto: 'second' - } - expect(objectToArray(obj)).toEqual(['first', 'second']) - }) - - it('should run through the object', () => { - const obj = { - pouet: 'first', - toto: 'second' - } - expect(objectMap(obj, (value, index) => { - return [index, value] - })).toEqual([['pouet', 'first'],['toto','second']]) - }) - +describe('Object sort Tests', () => { it('should sort the object', () => { const obj = { b: 'first', @@ -49,7 +82,9 @@ describe('Basic tests', () => { b: 'first' }) }) +}) +describe('Object Clone Tests', () => { it('should clone the object', () => { const obj = { pouet: 'first', @@ -71,7 +106,39 @@ describe('Basic tests', () => { clone.toto = 'third' expect(clone).not.toEqual(obj) }) -}); +}) + +describe('Object Set Tests', () => { + it('set the value of an empty object', () => { + const obj = {} + objectSet(obj, ['test'], true) + expect(obj).toEqual({test: true}) + }) + + it('set the deep value of an empty object', () => { + const obj = {} + objectSet(obj, ['test', 'pouet'], true) + expect(obj).toEqual({test: {'pouet': true}}) + }) + + it('set the deep first array value of an empty object', () => { + const obj = {} + objectSet(obj, ['test', 0], true) + expect(obj).toEqual({test: [true]}) + }) + + it('set the deep any array value of an empty object', () => { + const obj = {} + objectSet(obj, ['test', 2], true) + expect(obj).toEqual({test: [undefined, undefined, true]}) + }) + + it('delete the deep value of an object', () => { + const obj = {test: {pouet: true}} + objectSet(obj, ['test', 'pouet'], undefined) + expect(obj).toEqual({test: {}}) + }) +}) describe('Object Equal Test', () => { it('should be equal', () => { @@ -83,6 +150,13 @@ describe('Object Equal Test', () => { it('should be deeply equal', () => { expect(objectEqual({pouet: {is: true}}, {pouet: {is: true}})).toBe(true) }) + it('should not be equal if lengths are differents', () => { + expect(objectEqual({pouet: true, added: true }, {pouet: true})).toBe(false) + expect(objectEqual({pouet: true }, {pouet: true, added: true})).toBe(false) + }) + it('should not be equal if lengths are equal but content different', () => { + expect(objectEqual({pouet: true, added: true }, {pouet: true, removed: true})).toBe(false) + }) it('should not be deeply equal', () => { expect(objectEqual({pouet: {is: true}}, {pouet: {is: false}})).toBe(false) }) diff --git a/packages/object-util/src/ObjectUtil.ts b/packages/object-util/src/ObjectUtil.ts index d4f496d..1ac240c 100644 --- a/packages/object-util/src/ObjectUtil.ts +++ b/packages/object-util/src/ObjectUtil.ts @@ -1,43 +1,84 @@ -export const objectMap = (items: Record, fn: (el: T, key: string) => J) => { +/** + * Remap an object to an array through a function + * + * (Same as Array.map) + * @param obj the object to remap + * @param fn the fn to run + */ +export function objectMap(obj: Record, fn: (value: T, key: string) => J): Array { const list: Array = [] - objectLoop(items, (item, key) => { + objectLoop(obj, (item, key) => { list.push(fn(item, key)) }) return list } -export const objectLoop = (items: Record, fn: (el: T, key: string) => void | boolean) => { - let res: void | boolean - for (const key in items) { - if (!Object.prototype.hasOwnProperty.call(items, key)) { +/** + * Loop through the object + * @param obj the object to loop through + * @param fn the function to run for each childs + */ +export function objectLoop(obj: Record, fn: (value: T, key: string) => boolean | void): boolean { + for (const key in obj) { + if (!Object.prototype.hasOwnProperty.call(obj, key)) { continue } - res = fn(items[key], key) - if (typeof res === 'boolean' && !res) { - return res + const stop = fn(obj[key], key) + if (stop === false) { + return false } } - return res + return true } +/** + * Transform an object to an array removing the keys + * @param obj the object to transform + */ export function objectToArray(obj: Record): Array { return Object.values(obj) } -export function objectSize(obj: Record) { - return Object.keys(obj).length +/** + * return the keys of th object + * @param obj the object + */ +export function objectKeys(obj: Record): Array { + return Object.keys(obj) } -export function objectSort>(obj: Record, fn?: (a: string, b: string) => number): T { - const ordered: any = {}; - for (const key of Object.keys(obj).sort(fn)) { +/** + * return the length of an object + * @param obj the object + */ +export function objectSize(obj: Record): number { + return objectKeys(obj).length +} + +/** + * Sort an object by its keys + * + * Same as Array.sort + * @param obj the object to sort + * @param fn (Optionnal) the function to run to sort + */ +export function objectSort>( + obj: Record, + fn?: (a: string, b: string) => number +): T { + const ordered: any = {} + for (const key of objectKeys(obj).sort(fn)) { ordered[key] = obj[key] } return ordered } +/** + * Deeply clone an object + * @param obj the object to clone + */ export function cloneObject>(obj: T): T { - const clone: T = {} as any + const clone: Partial = {} objectLoop(obj, (value, key) => { if (typeof value === 'object' && value != null) { clone[key as Extract] = cloneObject(value) @@ -45,10 +86,20 @@ export function cloneObject>(obj: T): T { } clone[key as Extract] = value }) - return clone + return clone as T } -export function objectSet(obj: Record, path: Array, value: any) { +/** + * deeply set the value at the path given + * + * (Create sub object/array if not made) + * + * _NOTE: it is way quicker to use obj[path][path]... = value_ + * @param obj the object to set the value + * @param path the path + * @param value the value + */ +export function objectSet(obj: Record, path: Array, value: any): void { let pointer = obj for (let index = 0; index < path.length; index++) { const key = path[index] @@ -74,7 +125,15 @@ export function objectSet(obj: Record, path: Array } } +/** + * deeply compare objects and return if they are equal or not + * @param x the first object + * @param y the second object + */ export function objectEqual(x: Record, y: Record): boolean { + if (objectSize(x) !== objectSize(y)) { + return false + } const res = objectLoop(x, (item, key) => { if (!(key in y)) { return false @@ -85,9 +144,6 @@ export function objectEqual(x: Record, y: Record): boo } return item === item2 }) - if (typeof res !== 'boolean') { - return true - } return res } @@ -95,6 +151,7 @@ export default { objectMap, objectLoop, objectToArray, + objectKeys, objectSize, objectSort, cloneObject,