Cách sắp xếp chuỗi trong JavaScript


343

Tôi có một danh sách các đối tượng tôi muốn sắp xếp dựa trên một trường attrkiểu chuỗi. Tôi đã thử sử dụng-

list.sort(function (a, b) {
    return a.attr - b.attr
})

nhưng thấy rằng -dường như không hoạt động với các chuỗi trong JavaScript. Làm thế nào tôi có thể sắp xếp danh sách các đối tượng dựa trên một thuộc tính với chuỗi kiểu?


1
xem JavaScript case insensitive string comparisontrên stackoverflow.com/questions/2140627/ hy
Adrien Be

Đối với một giải pháp "quốc tế hóa" nhanh chóng (chỉ một phần tôi đoán vì điều này có thể không bao gồm tất cả các dấu trên thế giới), bạn có thể muốn bỏ qua các dấu, nghĩa là loại bỏ chúng. Sau đó, chỉ thực hiện so sánh chuỗi của bạn, xem Javascript : remove accents/diacritics in stringstrên stackoverflow.com/questions/990904/ Khăn
Adrien Be

2
Buồn cười là chính Jeff Atwood đã viết một bài đăng trên blog về vấn đề phổ biến này vào năm 2007, xem blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Câu trả lời:


619

Sử dụng String.prototype.localeComparemột ví dụ của bạn:

list.sort(function (a, b) {
    return ('' + a.attr).localeCompare(b.attr);
})

Chúng tôi buộc a.attr là một chuỗi để tránh ngoại lệ. localeCompaređã được hỗ trợ kể từ Internet Explorer 6 và Firefox 1. Bạn cũng có thể thấy đoạn mã sau được sử dụng không tôn trọng ngôn ngữ:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;

81
Trước khi bất cứ ai mắc phải sai lầm vội vàng như tôi đã làm, đó là địa phương e So sánh, không phải localCompare.
ento

12
Giải pháp đầu tiên sẽ xem xét "A" xuất hiện sau "z" nhưng trước "Z" vì nó đang so sánh giá trị ASCII của ký tự. localeCompare()không gặp phải vấn đề này nhưng không hiểu về số nên bạn sẽ nhận được ["1", "10", "2"] như với việc so sánh sắp xếp trong hầu hết các ngôn ngữ. nếu bạn muốn sắp xếp cho kết thúc trước UI của bạn, nhìn vào alphanum / sắp xếp tự nhiên thuật toán stackoverflow.com/questions/4340227/... hoặc stackoverflow.com/questions/4321829/...
Dead.Rabit

2
Lưu ý rằng localeCompare()chỉ được hỗ trợ trong các trình duyệt hiện đại: IE11 + tại thời điểm viết bài, hãy xem developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/
trộm

3
Không, ý tôi là dòng đầu tiên của bảng, @Adrien - IE hỗ trợ localeCompare()quay lại nhiều phiên bản, nhưng không hỗ trợ chỉ định ngôn ngữ cho đến phiên bản 11. Cũng lưu ý các câu hỏi mà Dead.Rabit liên quan đến.
Shog9

3
@ Shog9 xấu của tôi, có vẻ như nó được hỗ trợ kể từ IE6! xem (cuộn xuống / tìm kiếm theo phương thức localeCompare ()) trên msdn.microsoft.com/en-us/l Library / i / s4esdbwz (v = vs.94) .aspx . Mặc dù vậy, một điều cần lưu ý, trong các triển khai cũ, nơi chúng tôi không sử dụng các đối số tùy chọn và địa điểm (được sử dụng trước IE11) , thứ tự ngôn ngữ và sắp xếp được sử dụng hoàn toàn phụ thuộc vào việc thực hiện , nói cách khác: Firefox, Safari, Chrome & IE KHÔNG sắp xếp các chuỗi theo cùng một thứ tự. xem code.google.com/p/v8/issues/detail?id=459
Adrien Be

165

Câu trả lời cập nhật (tháng 10 năm 2014)

Tôi thực sự khó chịu về thứ tự sắp xếp chuỗi tự nhiên này vì vậy tôi mất khá nhiều thời gian để điều tra vấn đề này. Tôi hi vọng cái này giúp được.

Mẩu chuyện dài

localeCompare()hỗ trợ nhân vật là badass, chỉ cần sử dụng nó. Như được chỉ ra bởi Shog9, câu trả lời cho câu hỏi của bạn là:

return item1.attr.localeCompare(item2.attr);

Lỗi được tìm thấy trong tất cả các triển khai "thứ tự sắp xếp chuỗi tự nhiên" javascript tùy chỉnh

Có khá nhiều triển khai tùy chỉnh ngoài kia, cố gắng thực hiện so sánh chuỗi chính xác hơn được gọi là "thứ tự sắp xếp chuỗi tự nhiên"

Khi "chơi" với những triển khai này, tôi luôn nhận thấy một số lựa chọn "sắp xếp tự nhiên" kỳ lạ, hay đúng hơn là sai lầm (hoặc thiếu sót trong những trường hợp tốt nhất).

Thông thường, các ký tự đặc biệt (dấu cách, dấu gạch ngang, ký hiệu, dấu ngoặc, v.v.) không được xử lý chính xác.

Sau đó, bạn sẽ thấy chúng xuất hiện lẫn lộn ở những nơi khác nhau, điển hình có thể là:

  • một số sẽ nằm giữa chữ hoa 'Z' và chữ thường 'a'
  • một số sẽ nằm giữa '9' và chữ hoa 'A'
  • một số sẽ nằm sau chữ thường 'z'

Khi người ta mong đợi các nhân vật đặc biệt sẽ được "nhóm" lại với nhau ở một nơi, ngoại trừ nhân vật đặc biệt không gian có thể (sẽ luôn là nhân vật đầu tiên). Đó là, tất cả trước các số hoặc tất cả giữa các số và chữ cái (chữ thường & chữ hoa là "cùng nhau" với nhau) hoặc tất cả sau các chữ cái.

Kết luận của tôi là tất cả chúng đều không cung cấp một trật tự nhất quán khi tôi bắt đầu thêm các ký tự hầu như không bình thường (ví dụ: các ký tự có dấu phụ hoặc ký tự như dấu gạch ngang, dấu chấm than, v.v.).

Nghiên cứu về việc thực hiện tùy chỉnh:

Triển khai "thứ tự sắp xếp chuỗi tự nhiên" của trình duyệt thông qua localeCompare()

localeCompare()triển khai cũ nhất (không có đối số tùy chọn cục bộ và tùy chọn) được IE6 + hỗ trợ, xem http://msdn.microsoft.com/en-us/l Library / i / s4esdbwz (v = vs.94) .aspx (cuộn xuống localeCompare ( ) phương pháp). Việc xây dựng trong localeCompare()phương pháp làm một công việc tốt hơn nhiều lúc phân loại, thậm chí ký tự quốc tế & đặc biệt. Vấn đề duy nhất khi sử dụng localeCompare()phương thức này là "thứ tự miền và thứ tự được sử dụng hoàn toàn phụ thuộc vào việc thực hiện". Nói cách khác, khi sử dụng localeCompare, chẳng hạn như stringOne.localeCompare (stringTwo): Firefox, Safari, Chrome & IE có thứ tự sắp xếp khác nhau cho Chuỗi.

Nghiên cứu về các triển khai dựa trên trình duyệt:

Khó khăn của "chuỗi sắp xếp tự nhiên"

Thực hiện một thuật toán vững chắc (có nghĩa là: nhất quán nhưng cũng bao quát một phạm vi rộng các ký tự) là một nhiệm vụ rất khó khăn. UTF8 chứa hơn 2000 ký tựbao gồm hơn 120 tập lệnh (ngôn ngữ) . Cuối cùng, có một số đặc điểm kỹ thuật cho các tác vụ này, nó được gọi là "Thuật toán đối chiếu Unicode", có thể được tìm thấy tại http://www.unicode.org/reports/tr10/ . Bạn có thể tìm thêm thông tin về câu hỏi này về câu hỏi này, tôi đã đăng /software/257286/is-there-any-lingu-agnellect-specification-for-opes-natural-sorting-order

Kết luận cuối cùng

Vì vậy, xem xét mức độ hỗ trợ hiện tại được cung cấp bởi các triển khai tùy chỉnh javascript mà tôi đã gặp, có lẽ chúng ta sẽ không bao giờ thấy bất cứ điều gì gần gũi để hỗ trợ tất cả các ký tự & tập lệnh (ngôn ngữ) này. Do đó, tôi muốn sử dụng phương thức localeCompare () của trình duyệt. Đúng, nó có nhược điểm là không phù hợp giữa các trình duyệt nhưng thử nghiệm cơ bản cho thấy nó bao gồm phạm vi rộng hơn nhiều ký tự, cho phép sắp xếp thứ tự vững chắc và có ý nghĩa.

Vì vậy, như được chỉ ra bởi Shog9, câu trả lời cho câu hỏi của bạn là:

return item1.attr.localeCompare(item2.attr);

Đọc thêm:

Cảm ơn câu trả lời hay của Shog9, đưa tôi đi theo hướng "đúng" mà tôi tin


38

Trả lời (trong ECMAScript hiện đại)

list.sort((a, b) => (a.attr > b.attr) - (a.attr < b.attr))

Hoặc là

list.sort((a, b) => +(a.attr > b.attr) || -(a.attr < b.attr))

Sự miêu tả

Đúc một giá trị boolean cho một số mang lại các giá trị sau:

  • true -> 1
  • false -> 0

Hãy xem xét ba mẫu có thể:

  • x lớn hơn y: (x > y) - (y < x)-> 1 - 0->1
  • x bằng y: (x > y) - (y < x)-> 0 - 0->0
  • x nhỏ hơn y: (x > y) - (y < x)-> 0 - 1->-1

(Thay thế)

  • x lớn hơn y: +(x > y) || -(x < y)-> 1 || 0->1
  • x bằng y: +(x > y) || -(x < y)-> 0 || 0->0
  • x nhỏ hơn y: +(x > y) || -(x < y)-> 0 || -1->-1

Vì vậy, các logic này tương đương với các hàm so sánh sắp xếp điển hình.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;

1
Như tôi đã nhận xét về câu trả lời trước đó đã sử dụng thủ thuật này, các câu trả lời chỉ có mã có thể trở nên hữu ích hơn bằng cách giải thích cách chúng hoạt động.
Dan Dascalescu

Đã thêm mô tả
mpyw

Bạn có thể nhận xét về điều này tốt hơn hay tệ hơn localeCompare?
Ran Lottem

3
@RanLottem localeComparevà so sánh tiêu chuẩn mang lại kết quả khác nhau. Bạn mong đợi điều gì? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))sắp xếp theo thứ tự chữ cái không phân biệt chữ hoa chữ thường trong khi ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))thực hiện theo thứ tự mật mã
mpyw

Tôi thấy, đó dường như là một điểm chính. Bất kỳ ý tưởng về sự khác biệt hiệu suất?
Ran Lottem

13

Bạn nên sử dụng> hoặc <và == tại đây. Vì vậy, giải pháp sẽ là:

list.sort(function(item1, item2) {
    var val1 = item1.attr,
        val2 = item2.attr;
    if (val1 == val2) return 0;
    if (val1 > val2) return 1;
    if (val1 < val2) return -1;
});

1
Mặt khác, điều này sẽ không xử lý so sánh chuỗi so với số. Ví dụ: 'Z' <9 (sai), 'Z'> 9 (cũng sai ??), 'Z' == 9 (cũng sai !!). NaN ngớ ngẩn trong JavaScript ...
Kato

7

Chức năng mũi tên ternary lồng nhau

(a,b) => (a < b ? -1 : a > b ? 1 : 0)

7

vì các chuỗi có thể được so sánh trực tiếp trong javascript, điều này sẽ thực hiện công việc

list.sort(function (a, b) {
    return a.attr > b.attr ? 1: -1;
})

phép trừ trong hàm sắp xếp chỉ được sử dụng khi sắp xếp không theo thứ tự chữ cái (số) và tất nhiên nó không hoạt động với chuỗi


6

Tôi đã bị làm phiền về điều này từ lâu, vì vậy cuối cùng tôi đã nghiên cứu nó và đưa ra lý do dài dòng này cho lý do tại sao mọi thứ lại diễn ra như vậy.

Từ thông số kỹ thuật :

Section 11.9.4   The Strict Equals Operator ( === )

The production EqualityExpression : EqualityExpression === RelationalExpression
is evaluated as follows: 
- Let lref be the result of evaluating EqualityExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating RelationalExpression.
- Let rval be GetValue(rref).
- Return the result of performing the strict equality comparison 
  rval === lval. (See 11.9.6)

Vì vậy, bây giờ chúng ta đi đến 11.9.6

11.9.6   The Strict Equality Comparison Algorithm

The comparison x === y, where x and y are values, produces true or false. 
Such a comparison is performed as follows: 
- If Type(x) is different from Type(y), return false.
- If Type(x) is Undefined, return true.
- If Type(x) is Null, return true.
- If Type(x) is Number, then
...
- If Type(x) is String, then return true if x and y are exactly the 
  same sequence of characters (same length and same characters in 
  corresponding positions); otherwise, return false.

Đó là nó. Toán tử ba bằng được áp dụng cho các chuỗi trả về đúng iff các đối số chính xác là cùng một chuỗi (cùng độ dài và cùng ký tự ở các vị trí tương ứng).

Vì vậy, ===sẽ hoạt động trong các trường hợp khi chúng tôi cố gắng so sánh các chuỗi có thể đến từ các nguồn khác nhau, nhưng chúng tôi biết cuối cùng sẽ có cùng các giá trị - một kịch bản đủ phổ biến cho các chuỗi nội tuyến trong mã của chúng tôi. Ví dụ: nếu chúng ta có một biến có tên connection_statevà chúng ta muốn biết một trong những trạng thái sau đây ['connecting', 'connected', 'disconnecting', 'disconnected']là gì, chúng ta có thể trực tiếp sử dụng ===.

Nhưng còn nhiều hơn thế. Chỉ trên 11.9.4, có một lưu ý ngắn:

NOTE 4     
  Comparison of Strings uses a simple equality test on sequences of code 
  unit values. There is no attempt to use the more complex, semantically oriented
  definitions of character or string equality and collating order defined in the 
  Unicode specification. Therefore Strings values that are canonically equal
  according to the Unicode standard could test as unequal. In effect this 
  algorithm assumes that both Strings are already in normalized form.

Hừm. Gì bây giờ? Các chuỗi thu được từ bên ngoài có thể, và rất có thể sẽ, là unicodey kỳ lạ, và người hiền lành của chúng ta ===sẽ không làm cho họ công bằng. Để localeComparegiải cứu:

15.5.4.9   String.prototype.localeCompare (that)
    ...
    The actual return values are implementation-defined to permit implementers 
    to encode additional information in the value, but the function is required 
    to define a total ordering on all Strings and to return 0 when comparing
    Strings that are considered canonically equivalent by the Unicode standard. 

Chúng ta có thể về nhà bây giờ.

tl; dr;

Để so sánh các chuỗi trong javascript, sử dụng localeCompare; nếu bạn biết rằng các chuỗi không có các thành phần không phải ASCII vì chúng là, ví dụ, các hằng chương trình bên trong, thì ===cũng hoạt động.


0

Trong hoạt động của bạn trong câu hỏi ban đầu, bạn đang thực hiện thao tác sau:

item1.attr - item2.attr

Vì vậy, giả sử đó là những số (tức là item1.attr = "1", item2.attr = "2") Bạn vẫn có thể sử dụng toán tử "===" (hoặc các đánh giá nghiêm ngặt khác) miễn là bạn đảm bảo loại. Sau đây nên làm việc:

return parseInt(item1.attr) - parseInt(item2.attr);

Nếu chúng là alphaNumeric, thì hãy sử dụng localCompare ().


0
list.sort(function(item1, item2){
    return +(item1.attr > item2.attr) || +(item1.attr === item2.attr) - 1;
}) 

Cách họ làm việc mẫu:

+('aaa'>'bbb')||+('aaa'==='bbb')-1
+(false)||+(false)-1
0||0-1
-1

+('bbb'>'aaa')||+('bbb'==='aaa')-1
+(true)||+(false)-1
1||0-1
1

+('aaa'>'aaa')||+('aaa'==='aaa')-1
+(false)||+(true)-1
0||1-1
0

3
Câu trả lời chỉ có mã có thể được thực hiện hữu ích hơn bằng cách giải thích cách chúng hoạt động.
Dan Dascalescu

-2
<!doctype html>
<html>
<body>
<p id = "myString">zyxtspqnmdba</p>
<p id = "orderedString"></p>
<script>
var myString = document.getElementById("myString").innerHTML;
orderString(myString);
function orderString(str) {
    var i = 0;
    var myArray = str.split("");
    while (i < str.length){
        var j = i + 1;
        while (j < str.length) {
            if (myArray[j] < myArray[i]){
                var temp = myArray[i];
                myArray[i] = myArray[j];
                myArray[j] = temp;
            }
            j++;
        }
        i++;
    }
    var newString = myArray.join("");
    document.getElementById("orderedString").innerHTML = newString;
}
</script>
</body>
</html>

1
Vui lòng thêm một số thông tin về cách điều này sẽ giải quyết câu hỏi cho câu trả lời của bạn. Câu trả lời chỉ có mã không được hoan nghênh. Cảm ơn bạn.
wayneOS

Ở đây bạn muốn sắp xếp các ký tự trong một chuỗi, đó không phải là những gì được yêu cầu. Bạn có thể đạt được cách sắp xếp này chỉ bằng cách sử dụng "Array.sort", ví dụ: str.split (""). Sort () .join ("")
Alejadro Xalabarder

-2
var str = ['v','a','da','c','k','l']
var b = str.join('').split('').sort().reverse().join('')
console.log(b)

Mặc dù mã này có thể giải quyết câu hỏi, bao gồm giải thích về cách thức và lý do giải quyết vấn đề này thực sự sẽ giúp cải thiện chất lượng bài đăng của bạn và có thể dẫn đến nhiều lượt bình chọn hơn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai, không chỉ người hỏi bây giờ. Vui lòng chỉnh sửa câu trả lời của bạn để thêm giải thích và đưa ra dấu hiệu về những hạn chế và giả định được áp dụng.
Dave
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.