Skip to content

Instantly share code, notes, and snippets.

@wcarss
Last active May 13, 2021
Embed
What would you like to do?
a toy function to sort js arrays with sql-like syntax
// preamble: public domain; no warranty
//
// this is a toy -- it is slow, cannot handle unicode, keys w/
// spaces, nested keys, natural ordering, etc.
// orderBy('key1 [asc|1|desc|-1][, key2 [asc|1|desc|-1]][, etc]')
//
// write sql-like sort orders for arrays of objects, e.g.:
//
// arr.sort(orderBy('some_key desc, other_key asc');
// arr.sort(orderBy('some_key, other_key asc'));
// arr.sort(orderBy('a_key asc, b_key desc, c_key'));
//
// After reading some complicated sort functions, I wanted
// to quickly write something simple for 90% of our needs,
// just to see it.
//
// The key premise here is that the `cmp` function below can
// be chained to perform sorting by x, then y, then z, etc.,
// added to some string+array manip and default handling.
// the cmp function, aka the spaceship operator <=>
// (credit to https://stackoverflow.com/a/9175783)
const cmp = (a, b) => {
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
};
// the actual function that would be exported
const orderBy = sortspec => {
// handle just asc/desc out front for convenience
if (sortspec.toLowerCase() === 'desc') {
return (a, b) => cmp(b, a);
} else if (sortspec.toLowerCase() === 'asc') {
return (a, b) => cmp(a, b);
}
// split string up into array of 1 or 2 element arrays
const sorts = sortspec.split(',').map(sort => sort.trim().split(' '));
return (a, b) => sorts.reduce((acc, sort) => {
// accepts values asc, 1, desc, and -1 for orders
// defaults to ascending if order is invalid or not provided,
// sorting on an invalid key just hits undefined, preserves order
// also: no nested-key support!
const isAsc = sort.length === 1 || sort[1] === '1' || sort[1].toLowerCase() !== 'desc' && sort[1] !== '-1';
const left = isAsc ? a : b;
const right = isAsc ? b : a;
// progressively apply sorts; this won't exit asap
return acc || cmp(left[sort[0]], right[sort[0]]);
}, null);
};
// some example data to sort by:
const users = [
{
original_order: 0,
caseid: 'both empty',
some_prop: '',
fullname: '',
},
{
original_order: 1,
caseid: 'just empty name',
some_prop: 'test prop',
fullname: '',
},
{
original_order: 2,
caseid: 'empty prop name z',
some_prop: '',
fullname: 'empty prop test name z',
},
{
original_order: 3,
caseid: 'empty prop name a',
some_prop: '',
fullname: 'empty prop test name a',
},
{
original_order: 4,
caseid: 'same prop name b',
some_prop: 'test prop',
fullname: 'same prop test name b',
},
{
original_order: 5,
caseid: 'same prop name a',
some_prop: 'test prop',
fullname: 'same prop test name a',
},
{
original_order: 6,
caseid: 'prop + empty name',
some_prop: 'test prop',
fullname: '',
},
{
original_order: 7,
caseid: 'same prop name a again',
some_prop: 'test prop',
fullname: 'same prop test name a',
},
{
original_order: 8,
caseid: 'diff prop name b',
some_prop: 'other prop',
fullname: 'other prop test name b',
},
{
original_order: 9,
caseid: 'diff prop name a',
some_prop: 'other prop',
fullname: 'other prop test name a',
},
{
original_order: 9,
caseid: 'same prop test name a w/ unique prop',
some_prop: 'unique prop',
fullname: 'same prop test name a',
},
];
// some examples calls on the example data:
console.log(
'by undefined key (order should be preserved):',
[...users.sort(orderBy('count asc'))]
);
console.log(
'by some_prop descending, then by fullname',
[...users.sort(orderBy('some_prop desc, fullname'))]
);
console.log(
'by fullname ascending (note some_prop not ordered)',
[...users.sort(orderBy('fullname'))]
);
console.log(
'by fullname ascending, then by some_prop ascending',
[...users.sort(orderBy('fullname asc, some_prop asc'))]
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment