Skip to content

Commit

Permalink
1.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tamtamchik committed Aug 14, 2024
1 parent 48e1278 commit 411ece3
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 125 deletions.
42 changes: 23 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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": {
Expand Down
69 changes: 27 additions & 42 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, unknown>, key) => {
const value = (data as Record<string, unknown>)[key];
result[key] = getType(value) !== DataType.PRIMITIVE ? sort(value, asc) : value;
return result;
}, {});
}

return data;
}
92 changes: 32 additions & 60 deletions test/sort.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -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)
})
})

0 comments on commit 411ece3

Please sign in to comment.