'How to sort an array of objects correctly by an object's alphanumeric name value?
I am trying to sort an array of objects by the name field but the ordering is wrong.
The order I am getting is [1, 10, 11, 2, 20, 21, 3]
Instead of [1, 2, 3, .... 10, 11, 20, 21]
It is sorting but putting 10 ahead of 2
Here is the code I am currently using.
const arr = [
{
"name": "Action 10",
"color": "transparent",
"type": "components"
},
{
"name": "Action 11",
"color": "transparent",
"type": "components"
},
{
"name": "Action 2",
"color": "transparent",
"type": "components"
},
{
"name": "Action 20",
"color": "transparent",
"type": "components"
},
{
"name": "Action 21",
"color": "transparent",
"type": "components"
},
{
"name": "Action 3",
"color": "transparent",
"type": "components"
},
{
"name": "Action 4",
"color": "transparent",
"type": "components"
},
{
"name": "Action 5",
"color": "transparent",
"type": "components"
},
{
"name": "Action 6",
"color": "transparent",
"type": "components"
},
{
"name": "Action 1",
"color": "transparent",
"type": "components"
}
]
function sorter(a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
}
console.log(arr.sort(sorter));
Solution 1:[1]
One could give the numeric sorting option of String.prototype.localeCompare a try ...
const arr = [{
name: "Action 10", color: "transparent", type: "components"
}, {
name: "Action 11", color: "transparent", type: "components"
}, {
name: "Action 2", color: "transparent", type: "components"
}, {
name: "Action 20", color: "transparent", type: "components"
}, {
name: "Action 21", color: "transparent", type: "components"
}, {
name: "Action 3", color: "transparent", type: "components"
}, {
name: "Action 4", color: "transparent", type: "components"
}, {
name: "Action 5", color: "transparent", type: "components"
}, {
name: "Action 6", color: "transparent", type: "components"
}, {
name: "Action 1", color: "transparent", type: "components"
}];
function sorter(a, b) {
return a.name.localeCompare(b.name, undefined, { numeric: true });
}
console.log(arr.sort(sorter));
.as-console-wrapper { min-height: 100%!important; top: 0; }
Solution 2:[2]
The ordering is correct. The string "Action 10" collates greater than Action 1 and less than Action 2, because string comparison is done on a character by character basis.
The algorithm for string comparision is found here: https://262.ecma-international.org/5.1/#sec-11.8.5
[If] both px and py are Strings
If py is a prefix of px, return
false. (A String value p is a prefix of String value q if q can be the result of concatenating p and some other String r. Note that any String is a prefix of itself, because r may be the empty String.)If px is a prefix of py, return
true.Let k be the smallest nonnegative integer such that the character at position k within px is different from the character at position k within py. (There must be such a k, for neither String is a prefix of the other.)
Let m be the integer that is the code unit value for the character at position k within px.
Let n be the integer that is the code unit value for the character at position k within py.
If m < n, return
true. Otherwise, returnfalse.
Or, as a javascript function, the expression str1 < str2 is evaluated as if this function were invoked (not that anybody sane would implement the algorithm as described in the spec):
function lt( px, py ) {
const prefixOf = (x,y) => x.slice(0,y.length) === y;
if ( prefixOf(px,py) ) return false;
if ( prefixOf(py,px) ) return true;
let k = 0 ;
while ( px[k] === py[k] ) {
++k;
}
m = px.charCodeAt(k);
n = py.charCodeAt(k);
return m < n ? true : false;
}
If you want to order things according to the semantic meaning of name, you'll need to partition the string into a list of its non-numeric and numeric segments, convert the numeric bits into numbers, and then compare the segments in order from left to right.
Solution 3:[3]
You can use regular expressions:
const sorter = (a, b) => +a.name.match(/\d+/) - +b.name.match(/\d+/);
DEMO
const arr = [{"name": "Action 10","color": "transparent","type": "components"}, {"name": "Action 11","color": "transparent","type": "components"}, {"name": "Action 2","color": "transparent","type": "components"}, {"name": "Action 20","color": "transparent","type": "components"}, {"name": "Action 21","color": "transparent","type": "components"}, {"name": "Action 3","color": "transparent","type": "components"}, {"name": "Action 4","color": "transparent","type": "components"}, {"name": "Action 5","color": "transparent","type": "components"}, {"name": "Action 6","color": "transparent","type": "components"}, {"name": "Action 1","color": "transparent","type": "components"}]
function sorter(a, b) {
return +a.name.match(/\d+/)[0] - +b.name.match(/\d+/)[0];
}
console.log(arr.sort(sorter));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Peter Seliger |
| Solution 2 | Nicholas Carey |
| Solution 3 |
