Javascript: loại chuỗi ký tự tự nhiên


173

Tôi đang tìm cách dễ nhất để sắp xếp một mảng bao gồm các số và văn bản, và sự kết hợp của các mảng.

Ví dụ

'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'

trở thành

'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'

Điều này sẽ được sử dụng kết hợp với giải pháp cho một câu hỏi khác mà tôi đã hỏi ở đây .

Bản thân hàm sắp xếp hoạt động, cái tôi cần là một hàm có thể nói rằng '19asd' nhỏ hơn '123asd'.

Tôi đang viết cái này bằng JavaScript.

Chỉnh sửa: như adormitu đã chỉ ra, những gì tôi đang tìm kiếm là một chức năng để sắp xếp tự nhiên


xem thêm How do you do string comparison in JavaScript?trên stackoverflow.com/questions/51165/ khăn
Adrien Be

1
Câu hỏi ban đầu đã được hỏi vào năm 2010, vì vậy nó sẽ không gây ngạc nhiên :)
ptrn

Bản sao có thể có của Cách sắp xếp chuỗi trong JavaScript
feeela

Câu trả lời:


314

Điều này hiện có thể có trong các trình duyệt hiện đại sử dụng localeCompare. Bằng cách vượt qua numeric: truetùy chọn, nó sẽ thông minh nhận ra các con số. Bạn có thể sử dụng không phân biệt chữ hoa chữ thường sensitivity: 'base'. Đã thử nghiệm trong Chrome, Firefox và IE11.

Đây là một ví dụ. Nó trả về 1, nghĩa là 10 đi sau 2:

'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})

Để thực hiện khi sắp xếp số lượng lớn các chuỗi, bài viết cho biết:

Khi so sánh số lượng lớn các chuỗi, chẳng hạn như trong việc sắp xếp các mảng lớn, tốt hơn là tạo một đối tượng Intl.Collator và sử dụng hàm được cung cấp bởi thuộc tính so sánh của nó. Liên kết tài liệu

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));


12
Nếu bạn muốn sắp xếp một mảng các đối tượng, bạn cũng có thể sử dụng Collator: codepen.io/TimPietrusky/pen/rKzoGN
TimPietrusky

2
Để làm rõ nhận xét trên: "Nếu đối số cục bộ không được cung cấp hoặc không được xác định, ngôn ngữ mặc định của thời gian chạy được sử dụng."
gkiely

46

Vì vậy, bạn cần một loại tự nhiên ?

Nếu vậy, có lẽ kịch bản này của Brian Huisman dựa trên tác phẩm của David koelle sẽ là thứ bạn cần.

Có vẻ như giải pháp của Brian Huisman hiện được lưu trữ trực tiếp trên blog của David Koelle:


Chính xác, sắp xếp tự nhiên là những gì tôi đang tìm kiếm. Tôi sẽ xem liên kết bạn đã gửi, cảm ơn
ptrn

Đó là một loại rất không tự nhiên. Nó không tạo ra một loại chữ cái.
tchrist

@tchrist: ý của bạn là "nó không tạo ra một loại chữ cái nào?"
Adrien Be

Nó hoạt động tốt nhưng nó không xử lý số âm một cách chính xác. Tức là: nó sẽ tạo ra ['-1'. '-2', '0', '1', '2'].
adrianboimvaser

2
@mhitza mã này dường như làm một công việc tốt github.com/litejs/natural-compare-lite thấy một thử nghiệm nhanh jsbin.com/bevututodavi/1/edit?js,console
Adrien Hãy

23

Để so sánh các giá trị, bạn có thể sử dụng phương pháp so sánh-

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;
}

Nhưng đối với tốc độ sắp xếp một mảng, hãy xử lý mảng trước khi sắp xếp, do đó bạn chỉ phải thực hiện chuyển đổi chữ thường và biểu thức chính quy một lần thay vì trong mỗi bước thông qua sắp xếp.

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];
    }
}

điều này sẽ làm việc trong trường hợp của tôi, với mảng bên trong quyết định thứ tự của cái bên ngoài?
ptrn

Có gì String.prototype.tlc()? Đây có phải là mã của riêng bạn hay bạn đã lấy nó từ đâu đó? Nếu sau này, xin vui lòng liên kết đến trang.
Andy E

xin lỗi về sai lầm - sửa chữa, cảm ơn bạn. Nếu bạn muốn [1] và b [1] để điều khiển sắp xếp, hãy sử dụng a = String (a [1]). ToLowerCase (); b = Chuỗi (b [1]). toLowerCase ();
kennebec

Tôi vừa có một danh sách dữ liệu mà tôi muốn sắp xếp, nghĩ rằng nó sẽ dễ thực hiện trong bảng điều khiển Chrome Dev Tools - cảm ơn vì chức năng này!
ajh158

9

Nếu bạn có một mảng các đối tượng bạn có thể làm như thế này:

myArrayObjects = myArrayObjects.sort(function(a, b) {
  return a.name.localeCompare(b.name, undefined, {
    numeric: true,
    sensitivity: 'base'
  });
});


1
Câu trả lời hoàn hảo! Cảm ơn bạn.
hubert17

5

Thư viện đầy đủ tính năng nhất để xử lý việc này vào năm 2019 dường như là trật tự tự nhiên .

const { orderBy } = require('natural-orderby')

const unordered = [
  '123asd',
  '19asd',
  '12345asd',
  'asd123',
  'asd12'
]

const ordered = orderBy(unordered)

// [ '19asd',
//   '123asd',
//   '12345asd',
//   'asd12',
//   'asd123' ]

Nó không chỉ lấy các chuỗi của chuỗi, mà còn có thể sắp xếp theo giá trị của một khóa nhất định trong một mảng các đối tượng. Nó cũng có thể tự động xác định và sắp xếp các chuỗi: tiền tệ, ngày tháng, tiền tệ và một loạt các thứ khác.

Đáng ngạc nhiên, nó cũng chỉ có 1.6kB khi được nén.


2

Hãy tưởng tượng một hàm đệm 8 chữ số biến đổi:

  • '123asd' -> '00000123asd'
  • '19asd' -> '00000019asd'

Chúng tôi có thể sử dụng các chuỗi đệm để giúp chúng tôi sắp xếp '19asd' xuất hiện trước '123asd'.

Sử dụng biểu thức chính quy /\d+/gđể giúp tìm tất cả các số cần được đệm:

str.replace(/\d+/g, pad)

Sau đây trình bày cách sắp xếp bằng kỹ thuật này:

var list = [
    '123asd',
    '19asd',
    '12345asd',
    'asd123',
    'asd12'
];

function pad(n) { return ("00000000" + n).substr(-8); }
function natural_expand(a) { return a.replace(/\d+/g, pad) };
function natural_compare(a, b) {
    return natural_expand(a).localeCompare(natural_expand(b));
}

console.log(list.map(natural_expand).sort()); // intermediate values
console.log(list.sort(natural_compare)); // result

Các kết quả trung gian cho thấy thói quen Natural_Exand () làm gì và cung cấp cho bạn sự hiểu biết về cách hoạt động của thói quen Natural_compare tiếp theo:

[
  "00000019asd",
  "00000123asd",
  "00012345asd",
  "asd00000012",
  "asd00000123"
]

Đầu ra:

[
  "19asd",
  "123asd",
  "12345asd",
  "asd12",
  "asd123"
]

1

Dựa trên câu trả lời của @Adrien Hãy ở trên và sử dụng mã mà Brian Huisman & David koelle đã tạo, đây là một nguyên mẫu được sửa đổi sắp xếp cho một loạt các đối tượng:

//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"];
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.