From 411ece3203d3f0603e2cbac2594873d8f2a48738 Mon Sep 17 00:00:00 2001 From: Yuri Tkachenko Date: Wed, 14 Aug 2024 16:52:07 +0100 Subject: [PATCH] 1.2.0 --- package-lock.json | 42 ++++++++++++---------- package.json | 8 ++--- src/index.ts | 69 ++++++++++++++--------------------- test/sort.test.ts | 92 +++++++++++++++++------------------------------ 4 files changed, 86 insertions(+), 125 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60f81cc..5bbf5cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@tamtamchik/json-deep-sort", - "version": "1.1.4", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tamtamchik/json-deep-sort", - "version": "1.1.4", + "version": "1.2.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.12", - "@types/node": "^20.14.11", + "@types/node": "^22.3.0", "jest": "^29.7.0", - "ts-jest": "^29.2.3", - "tsup": "^8.2.2", + "ts-jest": "^29.2.4", + "tsup": "^8.2.4", "typescript": "^5.5.4" } }, @@ -1785,12 +1785,13 @@ } }, "node_modules/@types/node": { - "version": "20.14.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", - "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", + "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.18.2" } }, "node_modules/@types/stack-utils": { @@ -4848,10 +4849,11 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.2.3", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.3.tgz", - "integrity": "sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==", + "version": "29.2.4", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.4.tgz", + "integrity": "sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==", "dev": true, + "license": "MIT", "dependencies": { "bs-logger": "0.x", "ejs": "^3.1.10", @@ -4908,10 +4910,11 @@ } }, "node_modules/tsup": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.2.2.tgz", - "integrity": "sha512-MufIuzdSt6HYPOeOtjUXLR4rqRJySi6XsRNZdwvjC2XR+xghsu2L3vSmYmX+k4S1mO6j0OlUEyVQ3Fc0H66XcA==", + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.2.4.tgz", + "integrity": "sha512-akpCPePnBnC/CXgRrcy72ZSntgIEUa1jN0oJbbvpALWKNOz1B7aM+UVDWGRGIO/T/PZugAESWDJUAb5FD48o8Q==", "dev": true, + "license": "MIT", "dependencies": { "bundle-require": "^5.0.0", "cac": "^6.7.14", @@ -5005,10 +5008,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", + "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==", + "dev": true, + "license": "MIT" }, "node_modules/update-browserslist-db": { "version": "1.1.0", diff --git a/package.json b/package.json index 17b7d23..0aff0ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tamtamchik/json-deep-sort", - "version": "1.1.4", + "version": "1.2.0", "description": "A comprehensive utility for sorting JSON objects by keys.", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -33,10 +33,10 @@ ], "devDependencies": { "@types/jest": "^29.5.12", - "@types/node": "^20.14.11", + "@types/node": "^22.3.0", "jest": "^29.7.0", - "ts-jest": "^29.2.3", - "tsup": "^8.2.2", + "ts-jest": "^29.2.4", + "tsup": "^8.2.4", "typescript": "^5.5.4" }, "exports": { diff --git a/src/index.ts b/src/index.ts index 8f26675..414a9a8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,62 +3,47 @@ * @enum {string} */ enum DataType { - OBJECT, - ARRAY, - STRING, - OTHER, + OBJECT = 'object', + ARRAY = 'array', + PRIMITIVE = 'primitive' } /** * Function to determine the data type of the provided data. - * @param {any} data - The data to be checked. + * @param {unknown} data - The data to be checked. * @returns {DataType} The DataType value corresponding to the data's type. */ -function getType (data: any): DataType { - if (Array.isArray(data)) { - return DataType.ARRAY - } else if (typeof data === 'object' && data !== null) { - return DataType.OBJECT - } else if (typeof data === 'string') { - return DataType.STRING - } else { - return DataType.OTHER - } -} - -/** - * Function to determine whether the provided data is a recursive type. - * @param {any} data - The data to be checked. - * @returns {boolean} Whether the data is a recursive type. - */ -function isRecursiveType (data: any): boolean { - return [DataType.OBJECT, DataType.ARRAY].includes(getType(data)) +function getType(data: unknown): DataType { + if (Array.isArray(data)) return DataType.ARRAY; + if (data !== null && typeof data === 'object') return DataType.OBJECT; + return DataType.PRIMITIVE; } /** * Function to sort JSON data. - * @param {any} data - The data to be sorted. + * @param {unknown} data - The data to be sorted. * @param {boolean} [asc=true] - Whether to sort in ascending order. - * @returns {any} The sorted data. - * @throws {Error} If the data is not an object or array. + * @returns {unknown} The sorted data. */ -export function sort (data: any, asc: boolean = true): any { - switch (getType(data)) { +export function sort(data: unknown, asc: boolean = true): unknown { + const type = getType(data); - case DataType.ARRAY: - return data.map((d: any) => isRecursiveType(d) ? sort(d, asc) : d) - - case DataType.OBJECT: - const keys = Object.keys(data).sort() - - if (!asc) keys.reverse() + if (type === DataType.ARRAY) { + return (data as unknown[]).map(item => + getType(item) !== DataType.PRIMITIVE ? sort(item, asc) : item + ); + } - return keys.reduce((newData: any, key: string) => { - newData[key] = isRecursiveType(data[key]) ? sort(data[key], asc) : data[key] - return newData - }, {}) + if (type === DataType.OBJECT) { + const sortedKeys = Object.keys(data as object).sort(); + if (!asc) sortedKeys.reverse(); - default: - throw new Error('Invalid data type: expected an object or array of objects.') + return sortedKeys.reduce((result: Record, key) => { + const value = (data as Record)[key]; + result[key] = getType(value) !== DataType.PRIMITIVE ? sort(value, asc) : value; + return result; + }, {}); } + + return data; } diff --git a/test/sort.test.ts b/test/sort.test.ts index 7e8b6c5..2999b0b 100644 --- a/test/sort.test.ts +++ b/test/sort.test.ts @@ -1,16 +1,21 @@ import { sort } from '../src' describe('sort', () => { - it('should sort an object', () => { - const input = { b: 'b', a: 'a' } - const expected = { a: 'a', b: 'b' } + it('should sort an object in ascending order by default', () => { + const input = { c: 'c', b: 'b', a: 'a' } + const expected = { a: 'a', b: 'b', c: 'c' } expect(sort(input)).toEqual(expected) }) - it('should not sort an simple array', () => { - const input = ['a', 'b'] - const expected = ['a', 'b'] - expect(sort(input)).toEqual(expected) + it('should sort an object in descending order when specified', () => { + const input = { a: 'a', b: 'b', c: 'c' } + const expected = { c: 'c', b: 'b', a: 'a' } + expect(sort(input, false)).toEqual(expected) + }) + + it('should not modify an array of primitives', () => { + const input = ['b', 'a', 'c'] + expect(sort(input)).toEqual(input) }) it('should sort an array of objects', () => { @@ -25,74 +30,41 @@ describe('sort', () => { expect(sort(input)).toEqual(expected) }) - it('should sort an object with array values', () => { + it('should sort nested objects', () => { const input = { - b: ['b', 'a'], - a: ['d', 'c', 'b', 'a'], + b: { z: 'z', y: 'y' }, + a: { x: 'x', w: 'w' }, } const expected = { - a: ['d', 'c', 'b', 'a'], - b: ['b', 'a'], + a: { w: 'w', x: 'x' }, + b: { y: 'y', z: 'z' }, } expect(sort(input)).toEqual(expected) }) - it('should sort an object with nested objects', () => { + it('should handle mixed nested structures', () => { const input = { - a: 'a', - b: { - b: 'b', - a: 'a', - }, + b: ['b', 'a'], + a: { d: 'd', c: 'c' }, } const expected = { - a: 'a', - b: { - a: 'a', - b: 'b', - }, + a: { c: 'c', d: 'd' }, + b: ['b', 'a'], } expect(sort(input)).toEqual(expected) }) - it('should not sort an array of simple arrays', () => { - const input = [ - ['b', 'a'], - ['d', 'c', 'b', 'a'], - ] - const expected = [ - ['b', 'a'], - ['d', 'c', 'b', 'a'], - ] - expect(sort(input)).toEqual(expected) + it('should not throw an error for primitive inputs', () => { + expect(() => sort('string')).not.toThrow() + expect(() => sort(123)).not.toThrow() + expect(() => sort(null)).not.toThrow() + expect(() => sort(undefined)).not.toThrow() }) - it('should sort an array containing objects', () => { - const input = [ - { b: 'b', a: 'a' }, - { d: 'd', c: 'c', b: 'b', a: 'a' }, - ] - const expected = [ - { a: 'a', b: 'b' }, - { a: 'a', b: 'b', c: 'c', d: 'd' }, - ] - expect(sort(input)).toEqual(expected) - }) - - it('should sort an object in descending order', () => { - const input = { b: 'b', a: 'a', c: 'c' } - const expected = { c: 'c', b: 'b', a: 'a' } - expect(sort(input, false)).toEqual(expected) - }) - - it('should throw an error for non-object, non-array input', () => { - const input = 'string' - expect(() => sort(input)).toThrow('Invalid data type: expected an object or array of objects.') - - const inputNumber = 123 - expect(() => sort(inputNumber)).toThrow('Invalid data type: expected an object or array of objects.') - - const inputNull = null - expect(() => sort(inputNull)).toThrow('Invalid data type: expected an object or array of objects.') + it('should return primitive inputs unchanged', () => { + expect(sort('string')).toBe('string') + expect(sort(123)).toBe(123) + expect(sort(null)).toBe(null) + expect(sort(undefined)).toBe(undefined) }) })