Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions dist/js/shared/object.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ export declare function flattenObject(obj: NameValueObject, levelSeparator?: str
* Sort object keys alphabetically
*/
export declare function sortKeysDeepForObject<T>(obj: T): T;
/**
* Sort object keys alphabetically with excluded keys first (original order), then the rest sorted via sortKeysDeepForObject.
* Excluded keys are removed from the sort order; the remaining key set is sorted with sortKeysDeepForObject.
* @param obj - Object to sort (recursively)
* @param excludeKeys - Keys to leave out of sorting; these appear first in their original order at each level (default: [])
*/
export declare function sortKeysDeepForObjectWithExclude<T>(obj: T, excludeKeys?: string[]): T;
interface Tree<T = string> {
[key: string]: Tree<T> | T[];
}
Expand Down
29 changes: 28 additions & 1 deletion dist/js/shared/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.flattenNestedObjects = exports.mergeTerminalNodes = exports.sortKeysDeepForObject = exports.flattenObject = exports.stringifyObject = exports.renameKeysForObject = exports.convertKeysToCamelCaseForObject = exports.getOneMatchFromObject = exports.safeMakeObject = void 0;
exports.flattenNestedObjects = exports.mergeTerminalNodes = exports.sortKeysDeepForObjectWithExclude = exports.sortKeysDeepForObject = exports.flattenObject = exports.stringifyObject = exports.renameKeysForObject = exports.convertKeysToCamelCaseForObject = exports.getOneMatchFromObject = exports.safeMakeObject = void 0;
const camelCase_1 = __importDefault(require("lodash/camelCase"));
const filter_1 = __importDefault(require("lodash/filter"));
const isArray_1 = __importDefault(require("lodash/isArray"));
Expand Down Expand Up @@ -164,6 +164,33 @@ function sortKeysDeepForObject(obj) {
return obj;
}
exports.sortKeysDeepForObject = sortKeysDeepForObject;
/**
* Recursive helper: excluded keys first, then sortKeysDeepForObject(rest). Exclude set optional.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function sortWithExcludeRecursive(o, excludeSet) {
if (Array.isArray(o)) {
return o.map((item) => sortWithExcludeRecursive(item, excludeSet));
}
if ((0, isObject_1.default)(o)) {
const keys = Object.keys(o);
const excluded = keys.filter((k) => { var _a; return (_a = excludeSet === null || excludeSet === void 0 ? void 0 : excludeSet.has(k)) !== null && _a !== void 0 ? _a : false; });
const toSort = keys.filter((k) => { var _a; return !((_a = excludeSet === null || excludeSet === void 0 ? void 0 : excludeSet.has(k)) !== null && _a !== void 0 ? _a : false); });
toSort.sort();
const orderedKeys = [...excluded, ...toSort];
const result = {};
for (const key of orderedKeys) {
result[key] = sortWithExcludeRecursive(o[key], excludeSet);
}
return result;
}
return o;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function sortKeysDeepForObjectWithExclude(obj, excludeKeys = []) {
return sortWithExcludeRecursive(obj, new Set(excludeKeys));
}
exports.sortKeysDeepForObjectWithExclude = sortKeysDeepForObjectWithExclude;
function isTreeObject(value) {
return (0, isPlainObject_1.default)(value);
}
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

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

36 changes: 36 additions & 0 deletions src/js/shared/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,42 @@ export function sortKeysDeepForObject(obj: any): any {
return obj;
}

/**
* Recursive helper: excluded keys first, then sortKeysDeepForObject(rest). Exclude set optional.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function sortWithExcludeRecursive(o: any, excludeSet?: Set<string> | null): any {
if (Array.isArray(o)) {
return o.map((item) => sortWithExcludeRecursive(item, excludeSet));
}
if (isObject(o)) {
const keys = Object.keys(o);
const excluded = keys.filter((k) => excludeSet?.has(k) ?? false);
const toSort = keys.filter((k) => !(excludeSet?.has(k) ?? false));
toSort.sort();
const orderedKeys = [...excluded, ...toSort];
const result: Record<string, unknown> = {};
for (const key of orderedKeys) {
result[key] = sortWithExcludeRecursive(o[key], excludeSet);
}
return result;
}
return o;
}

/**
* Sort object keys alphabetically with excluded keys first (original order), then the rest sorted via sortKeysDeepForObject.
* Excluded keys are removed from the sort order; the remaining key set is sorted with sortKeysDeepForObject.
* @param obj - Object to sort (recursively)
* @param excludeKeys - Keys to leave out of sorting; these appear first in their original order at each level (default: [])
*/
export function sortKeysDeepForObjectWithExclude<T>(obj: T, excludeKeys?: string[]): T;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function sortKeysDeepForObjectWithExclude(obj: any, excludeKeys: string[] = []): any {
return sortWithExcludeRecursive(obj, new Set(excludeKeys));
}

interface Tree<T = string> {
[key: string]: Tree<T> | T[];
}
Expand Down
37 changes: 37 additions & 0 deletions tests/js/object.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
flattenNestedObjects,
flattenObject,
mergeTerminalNodes,
sortKeysDeepForObject,
sortKeysDeepForObjectWithExclude,
} from "../../src/js/shared/object";

describe("flattenObject", () => {
Expand Down Expand Up @@ -141,3 +143,38 @@ describe("flattenNestedObjects", () => {
expect(result).to.deep.equal({});
});
});

describe("sortKeysDeepForObject", () => {
it("sorts object keys alphabetically", () => {
const obj = { a: 1, c: 3, b: 2 };
const expectedObj = { a: 1, b: 2, c: 3 };

const result = sortKeysDeepForObject(obj);
expect(result).to.deep.equal(expectedObj);
});
});

describe("sortKeysDeepForObjectWithExclude", () => {
it("sorts object keys with excluded keys first", () => {
const obj = { a: 1, c: 3, b: 2 };
const excludeKeys = ["b"];
const expectedObj = { b: 2, a: 1, c: 3 };

const result = sortKeysDeepForObjectWithExclude(obj, excludeKeys);
expect(result).to.deep.equal(expectedObj);
});

it("sorts nested objects with excluded keys first at each level", () => {
const obj = {
y: { d: 5, e: 7, f: 9 },
x: { a: 1, c: 3, b: 2 },
};
const expectedObj = {
x: { a: 1, b: 2, c: 3 },
y: { f: 9, d: 5, e: 7 },
}

const result = sortKeysDeepForObjectWithExclude(obj, ["f"]);
expect(result).to.deep.equal(expectedObj);
});
});
Loading