Cara mengurutkan string dalam JavaScript

344

Saya memiliki daftar objek yang ingin saya urutkan berdasarkan bidang attrstring tipe. Saya mencoba menggunakan-

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

tetapi ternyata itu -tidak berfungsi dengan string dalam JavaScript. Bagaimana saya bisa mengurutkan daftar objek berdasarkan atribut dengan tipe string?

airportyh
sumber
1
lihat JavaScript case insensitive string comparisondi stackoverflow.com/questions/2140627/…
Adrien Be
Untuk solusi "internasionalisasi" cepat (hanya sebagian kurasa karena ini mungkin tidak mencakup semua aksen di dunia), Anda mungkin ingin mengabaikan aksen, yaitu menghapusnya. Maka hanya lakukan perbandingan string Anda, lihat Javascript : remove accents/diacritics in stringsdi stackoverflow.com/questions/990904/…
Adrien Be
2
Cukup lucu Jeff Atwood sendiri menulis posting blog tentang masalah umum ini pada tahun 2007, lihat blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Jawaban:

620

Gunakan String.prototype.localeCompareper contoh Anda:

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

Kami memaksa a.attr menjadi string untuk menghindari pengecualian. localeComparetelah didukung sejak Internet Explorer 6 dan Firefox 1. Anda juga dapat melihat kode berikut yang digunakan yang tidak menghormati lokal:

if (item1.attr < item2.attr)
  return -1;
if ( item1.attr > item2.attr)
  return 1;
return 0;
Shog9
sumber
81
Sebelum ada orang yang melakukan kesalahan tergesa-gesa seperti yang saya lakukan, ini adalah lokal dan bandingkan, bukan lokal.
ke
12
Solusi pertama akan mempertimbangkan "A" untuk datang setelah "z" tetapi sebelum "Z" karena melakukan perbandingan pada nilai karakter ASCII. localeCompare()tidak mengalami masalah ini tetapi tidak memahami angka sehingga Anda akan mendapatkan ["1", "10", "2"] seperti halnya dengan menyortir perbandingan dalam sebagian besar bahasa. jika Anda ingin mengurutkan untuk ujung depan UI Anda, lihat ke dalam algoritma alfanumerik / alamiah stackoverflow.com/questions/4340227/… atau stackoverflow.com/questions/4321829/…
Dead.Rabit
2
Catatan yang localeCompare()hanya didukung di peramban modern: IE11 + pada saat penulisan, lihat developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien Be
3
Tidak, maksud saya baris pertama dari tabel, @Adrien - IE mendukung localeCompare()banyak versi yang kembali, tetapi tidak mendukung menentukan lokal sampai versi 11. Perhatikan juga pertanyaan-pertanyaan yang terkait dengan Dead.Rabit.
Shog9
3
@ Shog9 salahku, sepertinya sudah didukung sejak IE6! lihat metode (scroll-down / search to localeCompare ()) di msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx . Satu hal yang perlu diperhatikan, Dalam implementasi lama di mana kita tidak menggunakan argumen locales dan opsi (yang digunakan sebelum IE11) , locale dan sort order yang digunakan sepenuhnya tergantung pada implementasi , dengan kata lain: Firefox, Safari, Chrome & IE do JANGAN mengurutkan string dalam urutan yang sama. lihat code.google.com/p/v8/issues/detail?id=459
Adrien Be
166

Jawaban yang diperbarui (Oktober 2014)

Saya benar-benar kesal tentang urutan penyortiran alami string ini sehingga saya mengambil cukup waktu untuk menyelidiki masalah ini. Saya harap ini membantu.

Singkat cerita

localeCompare()dukungan karakter adalah badass, gunakan saja. Seperti yang ditunjukkan oleh Shog9, jawaban untuk pertanyaan Anda adalah:

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

Bug yang ditemukan di semua implementasi "natural sort sort order" javascript khusus

Ada cukup banyak implementasi khusus di luar sana, mencoba melakukan perbandingan string yang lebih tepat disebut "urutan pengurutan string natural"

Ketika "bermain" dengan implementasi ini, saya selalu memperhatikan beberapa pilihan "natural sorting order" yang aneh, atau lebih tepatnya kesalahan (atau kelalaian dalam kasus terbaik).

Biasanya, karakter khusus (spasi, tanda hubung, tanda bintang, tanda kurung, dan sebagainya) tidak diproses dengan benar.

Anda kemudian akan menemukan mereka muncul bercampur di tempat yang berbeda, biasanya itu bisa:

  • beberapa akan berada di antara huruf besar 'Z' dan huruf kecil 'a'
  • beberapa akan berada di antara '9' dan huruf besar 'A'
  • beberapa akan menjadi setelah huruf kecil 'z'

Ketika seseorang akan mengharapkan karakter khusus untuk semua "dikelompokkan" bersama di satu tempat, kecuali untuk karakter khusus ruang mungkin (yang akan selalu menjadi karakter pertama). Yaitu, baik semua sebelum angka, atau semua antara angka dan huruf (huruf kecil & besar menjadi "bersama" satu demi satu), atau semua setelah huruf.

Kesimpulan saya adalah mereka semua gagal memberikan urutan yang konsisten ketika saya mulai menambahkan karakter yang hampir tidak biasa (mis. Karakter dengan diakritik atau karakter seperti tanda hubung, tanda seru, dan sebagainya).

Penelitian tentang implementasi kustom:

Implementasi asli "Urutan string alami" browser via localeCompare()

localeCompare()implementasi terlama (tanpa argumen lokal dan opsi) didukung oleh IE6 +, lihat http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (gulir ke bawah ke localeCompare ( ) metode). Built-in localeCompare()metode melakukan pekerjaan yang jauh lebih baik di menyortir, bahkan karakter internasional & khusus. Satu-satunya masalah menggunakan localeCompare()metode ini adalah "urutan lokal dan urutan yang digunakan sepenuhnya tergantung pada implementasi". Dengan kata lain, saat menggunakan localeCompare seperti stringOne.localeCompare (stringTwo): Firefox, Safari, Chrome & IE memiliki urutan pengurutan berbeda untuk Strings.

Penelitian tentang implementasi browser-asli:

Kesulitan "urutan penyortiran alami string"

Menerapkan algoritma yang solid (artinya: konsisten tetapi juga mencakup berbagai karakter) adalah tugas yang sangat sulit. UTF8 berisi lebih dari 2000 karakter & mencakup lebih dari 120 skrip (bahasa) . Akhirnya, ada beberapa spesifikasi untuk tugas ini, itu disebut "Unicode Collation Algorithm", yang dapat ditemukan di http://www.unicode.org/reports/tr10/ . Anda dapat menemukan informasi lebih lanjut tentang ini pada pertanyaan ini yang saya posting /software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Kesimpulan akhir

Jadi, mengingat tingkat dukungan saat ini yang diberikan oleh implementasi kustom javascript yang saya temui, kita mungkin tidak akan pernah melihat apa pun yang mendekati mendukung semua karakter & skrip (bahasa) ini. Oleh karena itu saya lebih suka menggunakan metode localeCompare () asli browser. Ya, ia memang memiliki kelemahan karena tidak konsisten di seluruh peramban, tetapi pengujian dasar menunjukkan bahwa ia mencakup jangkauan karakter yang jauh lebih luas, memungkinkan pesanan sortir yang padat & bermakna.

Jadi seperti yang ditunjukkan Shog9, jawaban untuk pertanyaan Anda adalah:

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

Bacaan lebih lanjut:

Berkat jawaban bagus Shog9, yang membuat saya ke arah yang "benar", saya percaya

Adrien Be
sumber
38

Jawab (dalam ECMAScript Modern)

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

Atau

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

Deskripsi

Casting nilai boolean ke angka menghasilkan yang berikut:

  • true -> 1
  • false -> 0

Pertimbangkan tiga pola yang mungkin:

  • x lebih besar dari y: (x > y) - (y < x)-> 1 - 0->1
  • x sama dengan y: (x > y) - (y < x)-> 0 - 0->0
  • x lebih kecil dari y: (x > y) - (y < x)-> 0 - 1->-1

(Alternatif)

  • x lebih besar dari y: +(x > y) || -(x < y)-> 1 || 0->1
  • x sama dengan y: +(x > y) || -(x < y)-> 0 || 0->0
  • x lebih kecil dari y: +(x > y) || -(x < y)-> 0 || -1->-1

Jadi logika ini setara dengan fungsi pembanding sortir tipikal.

if (x == y) {
    return 0;
}
return x > y ? 1 : -1;
mpyw
sumber
1
Saat saya mengomentari jawaban sebelumnya yang menggunakan trik ini, jawaban hanya kode dapat dibuat lebih bermanfaat dengan menjelaskan cara kerjanya.
Dan Dascalescu
Deskripsi tambahan
mpyw
Bisakah Anda mengomentari apakah ini lebih baik atau lebih buruk daripada localeCompare?
Ran Lottem
3
@RanLottem localeComparedan perbandingan standar menghasilkan hasil yang berbeda. Mana yang kamu harapkan ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))macam dalam urutan alfabet case-insensitive sementara ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))tidak dalam urutan codepoint
mpyw
Begitu ya, itu sepertinya menjadi poin utama. Adakah gagasan tentang perbedaan kinerja?
Ran Lottem
13

Anda harus menggunakan> atau <dan == di sini. Jadi solusinya adalah:

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;
});
airportyh
sumber
1
Di samping catatan, ini tidak akan menangani perbandingan string vs angka. Misalnya: 'Z' <9 (salah), 'Z'> 9 (juga salah ??), 'Z' == 9 (juga salah !!). NaN konyol dalam JavaScript ...
Kato
7

Fungsi panah ternary bersarang

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

karena string dapat dibandingkan secara langsung dalam javascript, ini akan melakukan pekerjaan

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

pengurangan dalam fungsi sortir digunakan hanya ketika sort non-abjad (numerik) diinginkan dan tentu saja itu tidak bekerja dengan string

Alejadro Xalabarder
sumber
6

Saya sudah lama terganggu tentang hal ini, jadi saya akhirnya meneliti ini dan memberi Anda alasan panjang lebar mengapa hal-hal seperti itu adanya.

Dari spec :

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)

Jadi sekarang kita pergi ke 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.

Itu dia. Operator triple sama dengan yang diterapkan pada string mengembalikan true jika argumen benar-benar string yang sama (panjang yang sama dan karakter yang sama di posisi yang sesuai).

Jadi ===akan berfungsi dalam kasus ketika kami mencoba membandingkan string yang mungkin berasal dari sumber yang berbeda, tetapi yang kami tahu pada akhirnya akan memiliki nilai yang sama - skenario yang cukup umum untuk string sebaris dalam kode kami. Sebagai contoh, jika kita memiliki variabel bernama connection_state, dan kita ingin tahu di mana salah satu dari keadaan berikut ['connecting', 'connected', 'disconnecting', 'disconnected']ini sekarang, kita dapat langsung menggunakan ===.

Tapi masih ada lagi. Tepat di atas 11.9.4, ada catatan singkat:

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.

Hmm. Apa sekarang? String yang diperoleh secara eksternal dapat, dan kemungkinan besar akan, aneh unik, dan lembut kita ===tidak akan melakukannya dengan adil. In datang localeCompareuntuk menyelamatkan:

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. 

Kita bisa pulang sekarang.

tl; dr;

Untuk membandingkan string dalam javascript, gunakan localeCompare; jika Anda tahu bahwa string tidak memiliki komponen non-ASCII karena mereka, misalnya, konstanta program internal, maka ===juga berfungsi.

Manav
sumber
0

Dalam operasi Anda dalam pertanyaan awal Anda, Anda melakukan operasi berikut:

item1.attr - item2.attr

Jadi, dengan asumsi itu adalah angka (yaitu item1.attr = "1", item2.attr = "2") Anda masih dapat menggunakan operator "===" (atau evaluator ketat lainnya) asalkan Anda memastikan tipe. Berikut ini harus bekerja:

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

Jika mereka alphaNumeric, maka gunakan localCompare ().

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

Cara kerja sampel:

+('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
Petr Varyagin
sumber
3
Jawaban khusus kode dapat dibuat lebih bermanfaat dengan menjelaskan cara kerjanya.
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>
Julio Munoz
sumber
1
Silakan tambahkan beberapa info tentang bagaimana ini akan menyelesaikan pertanyaan untuk jawaban Anda. Jawaban khusus kode tidak disambut. Terima kasih.
wayneOS
di sini Anda ingin memesan karakter dalam string, yang bukan apa yang diminta. Anda dapat mencapai pengurutan ini hanya dengan menggunakan "Array.sort" mis. 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)
Abdul
sumber
Sementara kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah akan sangat membantu untuk meningkatkan kualitas posting Anda, dan mungkin menghasilkan lebih banyak suara. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang. Harap edit jawaban Anda untuk menambahkan penjelasan, dan berikan indikasi batasan dan asumsi apa yang berlaku.
Dave