admin管理员组文章数量:1128333
I'm looking for the easiest way to sort an array that consists of numbers and text, and a combination of these.
E.g.,
'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'
turns into
'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'
This is going to be used in combination with the solution to another question I've asked here.
The sorting function in itself works, what I need is a function that can say that that '19asd' is smaller than '123asd'.
I'm writing this in JavaScript.
I'm looking for a function for natural sorting.
I'm looking for the easiest way to sort an array that consists of numbers and text, and a combination of these.
E.g.,
'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'
turns into
'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'
This is going to be used in combination with the solution to another question I've asked here.
The sorting function in itself works, what I need is a function that can say that that '19asd' is smaller than '123asd'.
I'm writing this in JavaScript.
I'm looking for a function for natural sorting.
Share Improve this question edited Jan 24, 2023 at 18:16 Peter Mortensen 31.6k22 gold badges109 silver badges133 bronze badges asked May 10, 2010 at 11:52 ptrnptrn 5,1926 gold badges29 silver badges30 bronze badges 4 |7 Answers
Reset to default 608This is now possible in modern browsers using localeCompare. By passing the numeric: true
option, it will smartly recognize numbers. You can do case-insensitive using sensitivity: 'base'
. It was tested in Chrome, Firefox, and Internet Explorer 11.
Here's an example. It returns 1
, meaning 10 goes after 2:
'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})
For performance when sorting large numbers of strings, the article says:
When comparing large numbers of strings, such as in sorting large arrays, it is better to create an Intl.Collator object and use the function provided by its compare property.
var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));
If you have an array of objects, you can do it like this:
myArrayObjects = myArrayObjects.sort(function(a, b) {
return a.name.localeCompare(b.name, undefined, {
numeric: true,
sensitivity: 'base'
});
});
var myArrayObjects = [{
"id": 1,
"name": "1 example"
},
{
"id": 2,
"name": "100 example"
},
{
"id": 3,
"name": "12 example"
},
{
"id": 4,
"name": "5 example"
},
]
myArrayObjects = myArrayObjects.sort(function(a, b) {
return a.name.localeCompare(b.name, undefined, {
numeric: true,
sensitivity: 'base'
});
});
console.log(myArrayObjects);
To compare values you can use a comparing method-
function naturalSorter(as, bs){
var a, b, a1, b1, i= 0, n, L,
rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g;
if(as=== bs) return 0;
a= as.toLowerCase().match(rx);
b= bs.toLowerCase().match(rx);
L= a.length;
while(i<L){
if(!b[i]) return 1;
a1= a[i],
b1= b[i++];
if(a1!== b1){
n= a1-b1;
if(!isNaN(n)) return n;
return a1>b1? 1:-1;
}
}
return b[i]? -1:0;
}
But for speed in sorting an array, rig the array before sorting, so you only have to do lower case conversions and the regular expression once instead of in every step through the sort.
function naturalSort(ar, index){
var L= ar.length, i, who, next,
isi= typeof index== 'number',
rx= /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.(\D+|$))/g;
function nSort(aa, bb){
var a= aa[0], b= bb[0], a1, b1, i= 0, n, L= a.length;
while(i<L){
if(!b[i]) return 1;
a1= a[i];
b1= b[i++];
if(a1!== b1){
n= a1-b1;
if(!isNaN(n)) return n;
return a1>b1? 1: -1;
}
}
return b[i]!= undefined? -1: 0;
}
for(i= 0; i<L; i++){
who= ar[i];
next= isi? ar[i][index] || '': who;
ar[i]= [String(next).toLowerCase().match(rx), who];
}
ar.sort(nSort);
for(i= 0; i<L; i++){
ar[i]= ar[i][1];
}
}
Imagine a number-zero-padding function n => n.padStart(8, "0")
that takes any number and pads it, i.e.
- "19" -> "00000019"
- "123" -> "00000123"
This function can be used to help sort the "19"
string so that it appears before the "123"
string.
Let's add a regex /\d+/g
creating the natural expansion function str => str.replace(/\d+/g, n => n.padStart(8, "0"))
which finds only number sections in a string and pads them, i.e.
- "19asd" -> "00000019asd"
- "123asd" -> "00000123asd"
Now, we can use this natural expansion function to help implement natural order sort:
const list = [
"123asd",
"19asd",
"12345asd",
"asd123",
"asd12"
];
const ne = str => str.replace(/\d+/g, n => n.padStart(8, "0"));
const nc = (a,b) => ne(a).localeCompare(ne(b));
console.log(list.map(ne).sort()); // intermediate values
console.log(list.sort(nc)); // result
The intermediate results demonstrated by list.map(ne).sort()
show what the ne
natural expansion function does. It implements number-zero-padding on only the number portions of the string and leaves the alphabet components unchanged.
[
"00000019asd",
"00000123asd",
"00012345asd",
"asd00000012",
"asd00000123"
]
The final version of solution implements a natural order comparator nc
implemented as (a,b) => ne(a).localeCompare(ne(b))
and uses it in list.sort(nc)
so things get ordered correctly:
[
"19asd",
"123asd",
"12345asd",
"asd12",
"asd123"
]
The most fully-featured library to handle this as of 2019 seems to be natural-orderby.
import { orderBy } from 'natural-orderby'
const unordered = [
'123asd',
'19asd',
'12345asd',
'asd123',
'asd12'
]
const ordered = orderBy(unordered)
// [ '19asd',
// '123asd',
// '12345asd',
// 'asd12',
// 'asd123' ]
It not only takes arrays of strings, but it can also sort by the value of a certain key in an array of objects. It can also automatically identify and sort strings of: currencies, dates, currency, and a bunch of other things.
Surprisingly, it's also only 1.6 kB when gzipped.
Building on kennebec's answer and using the code that Brian Huisman & David koelle created, here is a modified prototype sorting for an array of objects:
//Usage: unsortedArrayOfObjects.alphaNumObjectSort("name");
//Test Case: var unsortedArrayOfObjects = [{name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a10"}, {name: "a5"}, {name: "a13"}, {name: "a20"}, {name: "a8"}, {name: "8b7uaf5q11"}];
//Sorted: [{name: "8b7uaf5q11"}, {name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a5"}, {name: "a8"}, {name: "a10"}, {name: "a13"}, {name: "a20"}]
// **Sorts in place**
Array.prototype.alphaNumObjectSort = function(attribute, caseInsensitive) {
for (var z = 0, t; t = this[z]; z++) {
this[z].sortArray = new Array();
var x = 0, y = -1, n = 0, i, j;
while (i = (j = t[attribute].charAt(x++)).charCodeAt(0)) {
var m = (i == 46 || (i >=48 && i <= 57));
if (m !== n) {
this[z].sortArray[++y] = "";
n = m;
}
this[z].sortArray[y] += j;
}
}
this.sort(function(a, b) {
for (var x = 0, aa, bb; (aa = a.sortArray[x]) && (bb = b.sortArray[x]); x++) {
if (caseInsensitive) {
aa = aa.toLowerCase();
bb = bb.toLowerCase();
}
if (aa !== bb) {
var c = Number(aa), d = Number(bb);
if (c == aa && d == bb) {
return c - d;
} else {
return (aa > bb) ? 1 : -1;
}
}
}
return a.sortArray.length - b.sortArray.length;
});
for (var z = 0; z < this.length; z++) {
// Here we're deleting the unused "sortArray" instead of joining the string parts
delete this[z]["sortArray"];
}
}
I needed to sort an array of objects by multiple fields. After reading a bunch on the topic and some time working with various solutions; I ended up with something I didn't quite see anywhere all in one piece, so figured I'd share it here incase it helps anyone:
const orderBy = orders => (a, b) => {
const sortDirection = { asc: 1, desc: -1 };
const sortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
const totalOrders = orders.length;
for (let index = 0; index < totalOrders; index++) {
const { property, direction = 'desc' } = orders[index];
const directionInt = sortDirection[direction];
const compare = sortCollator.compare(a[property], b[property]);
if (compare < 0) return directionInt;
if (compare > 0) return -directionInt;
}
return 0;
};
// Example:
const unsorted = [
{ name: 'Bananas', count: 1 },
{ name: 'Apples', count: 3 },
{ name: 'Bananas', count: 3 },
{ name: 'bananas', count: 2 },
];
const sorted = unsorted.sort(orderBy([{ property: 'name' }, { property: 'count', direction: 'asc' }]));
console.log(sorted);
本文标签: sortingNatural sort of alphanumerical strings in JavaScriptStack Overflow
版权声明:本文标题:sorting - Natural sort of alphanumerical strings in JavaScript - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736720240a1949427.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
How do you do string comparison in JavaScript?
on stackoverflow.com/questions/51165/… – Adriano Commented Sep 26, 2014 at 12:19