Skip to content

Commit ee23303

Browse files
committed
feat: added getSortedObject()
1 parent 3cd6025 commit ee23303

2 files changed

Lines changed: 128 additions & 0 deletions

File tree

src/getSortedObject.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { isAnyObject } from './predicate/isAnyObject.js';
2+
3+
import type { CompareFn } from './types/functions.js';
4+
import type { KeyValueTuple } from './types/tuples.js';
5+
6+
const defaultCompareFunction: CompareFn<string | symbol> = (left, right) =>
7+
typeof left === 'symbol' || typeof right === 'symbol' ? 0 : left.localeCompare(right);
8+
9+
export function getSortedObject<Data extends object>(data: Data, compareFunction?: CompareFn<string | symbol>): Data;
10+
11+
export function getSortedObject<Data extends object>(
12+
data: Data[],
13+
compareFunction?: CompareFn<string | symbol>,
14+
): Data[];
15+
16+
export function getSortedObject<Data extends object>(
17+
data: Data | Data[],
18+
compareFunction: CompareFn<string | symbol> = defaultCompareFunction,
19+
): Data | Data[] {
20+
if (Array.isArray(data)) {
21+
return data.map((item) => getSortedObject(item, compareFunction));
22+
}
23+
24+
const entries = Reflect.ownKeys(data)
25+
.sort(compareFunction)
26+
.map((key): KeyValueTuple<string | symbol, unknown> => {
27+
const value = data[key as keyof typeof data];
28+
29+
return [key, isAnyObject(value) ? getSortedObject(value, compareFunction) : value];
30+
});
31+
32+
return Object.fromEntries(entries) as Data;
33+
}

test/getSortedObject.test.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { getSortedObject } from '../src/getSortedObject.js';
4+
import { isString } from '../src/predicate/isString.js';
5+
import { byReverseOf } from '../src/sort/factory/byReverseOf.js';
6+
7+
describe('getSortedObject()', () => {
8+
it('should return a sorted object', () => {
9+
const input = {
10+
b: 2,
11+
a: 1,
12+
c: 3,
13+
};
14+
15+
const serializedInput = JSON.stringify(input);
16+
17+
const expectedSerializedOutput = JSON.stringify({
18+
a: 1,
19+
b: 2,
20+
c: 3,
21+
});
22+
23+
const serializedOutput = getSortedObject(input);
24+
25+
expect(JSON.stringify(serializedOutput)).toEqual(expectedSerializedOutput);
26+
expect(serializedInput).not.toEqual(serializedOutput);
27+
});
28+
29+
it('handles nested objects', () => {
30+
const input = {
31+
b: {
32+
d: 4,
33+
c: 3,
34+
},
35+
a: [
36+
{ e: 5, f: 6 },
37+
{ h: 8, g: 7 },
38+
],
39+
c: 3,
40+
};
41+
42+
const serializedInput = JSON.stringify(input);
43+
44+
const expectedSerializedOutput = JSON.stringify({
45+
a: [
46+
{ e: 5, f: 6 },
47+
{ g: 7, h: 8 },
48+
],
49+
b: {
50+
c: 3,
51+
d: 4,
52+
},
53+
c: 3,
54+
});
55+
56+
const serializedOutput = getSortedObject(input);
57+
58+
expect(JSON.stringify(serializedOutput)).toEqual(expectedSerializedOutput);
59+
expect(serializedInput).not.toEqual(serializedOutput);
60+
});
61+
62+
it('handles empty objects', () => {
63+
expect(JSON.stringify(getSortedObject({}))).toEqual('{}');
64+
expect(JSON.stringify(getSortedObject([]))).toEqual('[]');
65+
});
66+
67+
it('handles array of objects', () => {
68+
expect(JSON.stringify(getSortedObject([{ b: 2, a: 1, c: 3 }]))).toEqual('[{"a":1,"b":2,"c":3}]');
69+
});
70+
71+
it('accepts custom comparator', () => {
72+
expect(
73+
JSON.stringify(
74+
getSortedObject(
75+
[{ b: 2, a: 1, c: 3 }],
76+
byReverseOf((left, right) => {
77+
return isString(left) && isString(right) ? left.localeCompare(right) : 0;
78+
}),
79+
),
80+
),
81+
).toEqual('[{"c":3,"b":2,"a":1}]');
82+
});
83+
84+
it('handles Symbol properties', () => {
85+
const symbolProperty = Symbol('property');
86+
87+
const input = {
88+
b: 2,
89+
a: 1,
90+
[symbolProperty]: 'symbolValue',
91+
};
92+
93+
expect(Reflect.ownKeys(getSortedObject(input))).toEqual(['a', 'b', symbolProperty]);
94+
});
95+
});

0 commit comments

Comments
 (0)