Skip to content

Instantly share code, notes, and snippets.

@geraldfullam
Last active January 30, 2020 19:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save geraldfullam/140979635ee8faa7e21f to your computer and use it in GitHub Desktop.
Save geraldfullam/140979635ee8faa7e21f to your computer and use it in GitHub Desktop.
Sort by, then by
<table id="t1"></table>
<table id="t2"></table>
<table id="t3"></table>
<table id="t4"></table>
/* "Sort by, then by"
// Based on this jsfiddle:
// http://jsfiddle.net/dFNva/1/
// as a response to this stackoverflow post:
// http://stackoverflow.com/a/979325/2502532
// Improvements include JSON deep access with path notation, 'then by' sorting.
// Description: Sort by object key, with optional reverse ordering, priming, and 'then by' sorting.
// Usage:
array.sort(
by(path[, reverse[, primer[, then]]])
);
// Examples:
// Sort by 'props.title', Ascending [A-Z]:
array.sort(by('props.title'));
// Sort by 'props.title', Descending [Z-A]:
array.sort(by('props.title'), true);
// Sort by 'props.title', Ascending [A-Z], Primer function converts strings to uppercase for case-insensitive comparison:
array.sort(
by('props.title', false, function (x) { return x.toUpperCase() })
);
// Sort by 'id', Descending [9-0] (reverse), Primer `parseFloat` converts to number before comparison;
// Then by 'props.title', Descending [Z-A], Primer function converts strings to uppercase for case-insensitive comparison:
// NOTE: sort order is inherited; setting to true in the secondary `by()` would return the reverse of the parent `by()` sort order.
array.sort(
by('id', true, parseFloat,
by('props.title', false, function (x) { return x.toUpperCase() })
)
);
*/
/* THE FUNCTION */
var by = function (path, reverse, primer, then) {
// Light weight json deep access
var get = function (obj, path) {
if (path) {
path = path.split('.');
for (var i = 0, len = path.length - 1; i < len; i++) {
obj = obj[path[i]];
};
return obj[path[len]];
}
return obj;
},
// Invokes primer function if provided
prime = function (obj) {
return primer ? primer(get(obj, path)) : get(obj, path);
};
// Actual sorting function to be returned to native .sort method
return function (a, b) {
var A = prime(a),
B = prime(b);
return (
(A < B) ? -1 :
(A > B) ? 1 :
// If A == B, then sort by supplemental 'by' function received as 'then'
(typeof then === 'function') ? then(a, b) : 0
) * [1,-1][+!!reverse];
};
};
/* THE ARRAY */
var places = [
{
"id": 0,
"props": {
"city": "Dallas",
"state": "TX",
"zip": 75001
}
}, {
"id": 1,
"props": {
"city": "Austin",
"state": "TX",
"zip": 78610
}
}, {
"id": 2,
"props": {
"city": "Beverly Hills",
"state": "CA",
"zip": 90210
}
}, {
"id": 3,
"props": {
"city": "Houston",
"state": "TX",
"zip": 77002
}
}, {
"id": 4,
"props": {
"city": "New York",
"state": "NY",
"zip": 10453
}
}, {
"id": 4,
"props": {
"city": "O'Neill",
"state": "NE",
"zip": 68763
}
}, {
"id": 4,
"props": {
"city": "Omaha",
"state": "NE",
"zip": 68183
}
}
];
/* THE EXAMPLES */
/* Sort by "state" A-Z */
places.sort(by('props.state'));
// Output
var t1 = document.getElementById('t1');
t1.innerHTML += '<caption>Sort by "state" A-Z</caption>';
for (var i = 0; i < places.length; i++) {
t1.innerHTML += '<tr><td>' + places[i].props.city + '</td><td class="sortby">' + places[i].props.state + '</td><td> ' + places[i].props.zip + '</td></tr>';
}
/* Sort by "state" Z-A (reverse) */
places.sort(by('props.state', true));
// Output
var t2 = document.getElementById('t2');
t2.innerHTML += '<caption>Sort by "state" Z-A (reverse)</caption>';
for (var i = 0; i < places.length; i++) {
t2.innerHTML += '<tr><td>' + places[i].props.city + '</td><td class="sortby">' + places[i].props.state + '</td><td> ' + places[i].props.zip + '</td></tr>';
}
/* Sort by 'state', Descending [Z-A] (reverse), Primer `parseFloat` converts to number before comparison;
// Then by 'zip', Descending [9-0], Primer function converts strings to uppercase before comparison.
// NOTE: sort order is inherited; setting to true here would return the reverse of the parent sort order. */
places.sort(
by('props.state', true, null,
by('props.zip', false, parseFloat)
)
);
// Output
var t3 = document.getElementById('t3');
t3.innerHTML += '<caption>Sort by "state" Z-A (reverse), then by "ZIP" 9-0 (reverse inherited, primer)</caption>';
for (i = 0; i < places.length; i++) {
t3.innerHTML += '<tr><td>' + places[i].props.city + '</td><td class="sortby">' + places[i].props.state + '</td><td class="thenby"> ' + places[i].props.zip + '</td></tr>';
}
/* Sort by 'city', Ascending [A-Z], Primer function converts strings to uppercase & removes non-word characters before comparison. */
places.sort(
by('props.city', false, function (x) {
return x.toUpperCase().replace(/\W/g, '');
})
);
// Output
var t4 = document.getElementById('t4');
t4.innerHTML += '<caption>Sort by "city" A-Z, case- & punctuation-insensitive (primer)</caption>';
for (i = 0; i < places.length; i++) {
t4.innerHTML += '<tr><td class="sortby">' + places[i].props.city + '</td><td>' + places[i].props.state + '</td><td> ' + places[i].props.zip + '</td></tr>';
}
html { font: normal 62.5% Arial, sans-serif; }
body { padding: 10px; }
table {
table-layout: fixed;
border-collapse: collapse;
font-size: 1.3em;
margin-bottom: 18px;
width: 100%;
border: none;
}
table caption {
text-align: left;
font-weight: bold;
font-size: 1.5rem;
}
table td {
color: #999;
padding: 9px;
vertical-align: middle;
text-align: left;
line-height: 1em;
border-bottom: 1px solid #bcbec0;
}
table .number {
text-align: center;
}
.sortby {
color: #333;
font-weight: bold;
}
.thenby {
color: #333;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment