Define a sorting function, that takes functions for augmented sorting
My favorite sorting function (which is more versatile than needed for just this task) can sort arrays of values or objects of any nested depth and accepts an optional function for priming the value, and an optional function for secondary "then by" sorting.
Sort by, then by 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];
};
};
Acknowledgement: The above function is my fork of a function that I found as a response to this Stack Overflow question.
Basic usage:
array.sort(
by(path[, reverse[, primer[, then]]])
);
Example usage with your array:
/* THE ARRAY */
var arr = ["10Regular", "18Regular", "14Long", "14Regular", "10Long", "16Long", "12Long", "20Regular", "16Regular", "12Regular", "18Long"];
/* THE EXAMPLE */
arr.sort(
by(null, false,
function (x) { // Primer
var y = x.substr(0,2);
return parseFloat(y);
},
by(null, false, // Secondary "then by" sort
function (x) { // Primer
var y = x.substr(2);
return y;
}
)
)
);
The idea here is to split your value into the two parts by which you wish to sort it.
Assuming that your string values will always begin with two-digit numbers, we define a priming function that uses .substr
to return just the first two digits. This primed value gets passed along to the return function in the closure just for the comparison, while preserving the original value to be sorted.
We then define a secondary "then by" sorting function which takes the same form as the first (you just pass in another instance of by()
). In this secondary by()
function, we define another priming function that uses .substr
again to return the second half of the string.
Complete working example with output:
/* 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 arr = ["10Regular", "18Regular", "14Long", "14Regular", "10Long", "16Long", "12Long", "20Regular", "16Regular", "12Regular", "18Long"];
/* THE EXAMPLE */
arr.sort(
by(null, false,
function (x) {
var y = x.substr(0,2);
return parseFloat(y);
},
by(null, false,
function (x) {
var y = x.substr(2);
return y;
}
)
)
);
// Output
var t1 = document.getElementById('t1');
t1.innerHTML += '<caption>Sort by, then by</caption>';
for (var i = 0; i < arr.length; i++) {
t1.innerHTML += '<tr><td>' + arr[i] + '</td></tr>';
}
html { font: normal 62.5% Arial, sans-serif; }
body { padding: 10px; }
table {
font-size: 1.3em;
margin-bottom: 18px;
width: 50%;
border: none;
}
table caption {
font-weight: bold;
font-size: 1.5rem;
}
table td {
color: #333;
padding: 9px;
vertical-align: middle;
text-align: left;
line-height: 1em;
border-bottom: 1px solid #bcbec0;
font: normal 1.3em monospace;
}
<table id="t1"></table>