Skip to content

Instantly share code, notes, and snippets.

@erkobridee
Last active August 6, 2025 12:23
Show Gist options
  • Save erkobridee/a7bcbc6c50044f9178c002f8eb2766aa to your computer and use it in GitHub Desktop.
Save erkobridee/a7bcbc6c50044f9178c002f8eb2766aa to your computer and use it in GitHub Desktop.
/*
dynamic sorting an object array
https://dev.to/fpaghar/donesort-an-array-of-objects-ways-in-javascript-48hl
*/
const SortDirection = {
Asc: 1,
Desc: -1
} as const;
type TSortDirectionKeys = keyof typeof SortDirection;
type TSortDirection = typeof SortDirection[TSortDirectionKeys];
type DynamicSortByFn<T, R = number | string> = (item: T) => R;
interface DynamicSortOption<T> {
by: DynamicSortByFn<T>;
direction?: TSortDirection;
isAlphabetical?: boolean;
};
const dynamicSort = <T = unknown>(collection: T[], sortOptions: DynamicSortOption<T>[]): T[] => {
const sortOptionsLength = sortOptions.length
return sortOptionsLength === 0 ? [...collection] : (
collection.toSorted((itemA, itemB) => {
for (let sortIndex = 0; sortIndex < sortOptionsLength; sortIndex++) {
const { by, direction = SortDirection.Asc, isAlphabetical = false } = sortOptions[sortIndex];
const a = direction === SortDirection.Asc ? by(itemA) : by(itemB);
const b = direction === SortDirection.Asc ? by(itemB) : by(itemA);
let diff = 0;
switch (typeof a) {
case 'number':
diff = (a as number) - (b as number);
break;
case 'string':
const aString = a as string;
const bString = b as string;
diff = (
isAlphabetical ?
aString.localeCompare(bString) :
aString === bString ?
0 :
aString > bString ? 1 : -1
);
break;
}
if ( diff !== 0 ) {
return diff;
}
}
return 0;
})
);
}
//---------------------------------------------------------------------//
// @begin: mocked data
const collection = [
{ code: '1-1:4.55.0', startDate: '2022-01-01', endDate: null },
{ code: '1-1:3.55.0', startDate: '2022-01-01', endDate: null },
{ code: '1-1:2.55.0', startDate: '2023-01-01', endDate: '2024-01-01' },
{ code: '1-1:1.55.0', startDate: '2023-01-01', endDate: '2024-01-01' },
];
// @end: mocked data
//---------------------------------------------------------------------//
const today = new Date()
const result = dynamicSort(collection, [
{ by: ( item ) => (item.endDate === null ? today : new Date(item.endDate)).getTime(), direction: SortDirection.Desc },
{ by: ( item ) => (item.startDate === null ? today : new Date(item.startDate)).getTime(), direction: SortDirection.Desc },
{ by: ( item ) => item.code, isAlphabetical: true },
]);
console.log( result );
/* output:
[{
"code": "1-1:3.55.0",
"startDate": "2022-01-01",
"endDate": null
}, {
"code": "1-1:4.55.0",
"startDate": "2022-01-01",
"endDate": null
}, {
"code": "1-1:1.55.0",
"startDate": "2023-01-01",
"endDate": "2024-01-01"
}, {
"code": "1-1:2.55.0",
"startDate": "2023-01-01",
"endDate": "2024-01-01"
}]
*/
// TypeScript Playground
// https://www.typescriptlang.org/play/?target=99&moduleResolution=99&module=200&ts=5.9.2#code/MYewdgzgLgBAyiATlAIgS0QU2FN4YC8MA3gFAwUwCCEwAXDAIwA05lKmtDAtI6QL4wAhhBihIUANylSUAJ4AHTDAAqCZOiw48YANKY5oogGsDIAGYx5Si-CSoM2XOGnXla+5qc7CVxZlt1By1nMABtDw1HbXB9QwBdaVl-GBQ5MCEAWzRgIIAhOQAxMAAeFWYYACVfMABXTIAjTEQYAB8YaEQ0MABzAD5fAAo0KExMhhUASkIByqTu0cRzIWBlNIzs3PsAeQVQsoGyCga5BnWsnPyi0pU+6QoAE2jQgH4JoK8YsHuYNAgqAA2CgAFkImrhgEIAW8YA0QCAAZghN8BElxNAYA90hctshfGVfLUwMYwCAAO5gPqDUAAxFfCZheIVCA7PY6CBnbGbIK7fa3RmTBnxGYkNjo2As5C89kAGUwvSgwN8kqg0vAEAAdIiFcCZBQsFBaogwB1WaEIHKdYQCEQAAwwF4wMIal00umhYUMQZsChu7zgDVQEBBTAPQbDUaZKgVEZjPLTAiHH2UcxIGCDRES+wASTAD0wAA9fLbJKbkLn80WSmXVWz1ZaeorSyqK4WANRt6ZHSg98UkWFyCpPEI+IgfZ46DU0YAx-5A0HgnJQ3zLAEQZSCIgqtWQMItvOFxJ6nuUPtCXzD-0mm1jzwTgPTh0DiNjKjTBgnF+ZeM-E9idWwA0F73teNp2FEI4PrQT6frG37vs+cFvkkf4UJmmJoOYlh2ihqEQGSIzAEqgxuLYQhdsmf6QuuMAAOR1I0zS0XQlGoU8WFDOeIgwAxTSINM3DpkB3G8c0ky-qhxxYEIxgSVRIjKLRnTdD0zGsVRAHCHAUBdL0vhcaIym9HJqF9g02m6T0vjCYZOkqbhkmPJh2HpupqF-ICIJgpgELLi8bmSUIFkqVqICQoiADCICZAoQhYIM5l2b074BahQVJVZN6wsFen+Y5jn2ix+WBTlVkDIlllPowMA8HwxUwOJqVSUislufwx5-ph6YYRxACEYH2hR+UGkaJrseYJkUO1f7taxI3GjAJbJvwkxsI1s0APQbdwO27Xt+0HYdR3HSdp27VtpBbTAAACTQ9N0DCZGFpgPJiQhQEIMh9n6Xy+GEbDEP++YMLRjC8HQAAsGoAKzQxqtq0cyH0aO9mAgwATLa6Po9wtpg3jiMwPKDwoKjDB1LSMD8KwFCA6AwN0WDjB0AAzDDcMI0jcWoGTdGY9juP44whPE6Tozk7UlPUwDQNo4z4Po+z8OE9A3Ni3LtH8yzguCyLebqxjWMQzrBNUzT-b0xrTN0IwSucx0yM8+LfNY9reO6xUou85rRsm8LZukEel0bTdxOPc9oZvR9wdnbHcfx-HF2kH2QYPEIcg1JgZKpKjgyrX2WAQJLsBEFiGyXPY1IIu6OgVP9tMDl6vyRg1IpfhqXujNaRAUwCT6p+nNU8VnOejO3neYJMkwaj0PkqGgmSYHnQ4gQw46QWAGocNB0sNycTdwa3ibpnBGqqyjXdZb3-cgGnGfkyP6vt+fTuT9Ps9QPPi-LxhG9r3eG8t6cGAAHPepxuqHwTAMU+ltZyeQXD5JcAIGA6VqBuVg8RGrJ3VAiTAoUeiDBgIXYuDVJBAA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment