JavaScript'te dizeler nasıl sıralanır?


344

attrTür dizesi alanına göre sıralamak istediğim nesnelerin bir listesi var . Kullanmayı denedim-

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

ancak -JavaScript'te dizelerle çalışmadığı anlaşılıyor. Nesne listesini type dizesine sahip bir özniteliğe göre nasıl sıralayabilirim?


1
bkz JavaScript case insensitive string comparison. stackoverflow.com/questions/2140627/…
Adrien Be

Hızlı bir "uluslararası" çözüm için (sadece kısmen dünyadaki tüm aksanları kapsamadığından sanırım), aksanları görmezden gelmek, yani bunları kaldırmak isteyebilirsiniz. O zaman sadece string karşılaştırmanızı yapın, bkz Javascript : remove accents/diacritics in strings. Stackoverflow.com/questions/990904/…
Adrien Be

2
Yeterince komik Jeff Atwood kendisi 2007 yılında bu ortak sorun hakkında bir blog yazısı yazdı, bkz. Blog.codinghorror.com/sorting-for-humans-natural-sort-order
Adrien Be

Yanıtlar:


621

String.prototype.localeCompareÖrnek başına a kullanın :

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

İstisnaları önlemek için a.attr öğesini bir dize olmaya zorlarız. Internet Explorer 6 ve Firefox 1'den berilocaleCompare desteklenmektedir. Yerel ayarlara uymayan aşağıdaki kodu da görebilirsiniz:

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

81
Herkes benimle aynı aceleci hata yapmadan önce, yerel e karşılaştırmak, localCompare değil.
ento

12
İlk çözüm, "A" nın "z" den sonra, "Z" den önce gelmesi için ASCII karakteri üzerinde bir karşılaştırma yapmayı düşünecektir. localeCompare()bu sorunla karşılaşmaz ancak sayısalları anlamadığından çoğu dilde sıralama karşılaştırmasında olduğu gibi ["1", "10", "2"] elde edersiniz. UI ön
ucunuz

2
localeCompare()Yalnızca modern tarayıcılarda desteklendiğini unutmayın : Yazarken IE11 +, bkz. Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Adrien

3
Hayır, tablonun ilk satırını kastediyorum, @Adrien - IE localeCompare()birçok sürümü geri almayı destekliyor , ancak sürüm 11'e kadar yerel ayarı belirtmeyi desteklemiyor . Dead.Rabit'in bağlandığı soruları da not edin.
Shog9

3
@ Shog9 benim kötü, IE6 beri desteklenmiş gibi görünüyor! msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx adresinde (aşağı kaydırma / localeCompare () yönteminde arama) konusuna bakın . Ancak dikkat edilmesi gereken bir nokta, yerel ayarları ve seçenekler bağımsız değişkenlerini (IE11'den önce kullanılan) kullanmadığımız eski uygulamalarda, kullanılan yerel ayar ve sıralama düzeni tamamen uygulamaya bağlıdır , başka bir deyişle : Firefox, Safari, Chrome ve IE Dizeleri aynı sırada sıralama DEĞİLDİR. bkz. code.google.com/p/v8/issues/detail?id=459
Adrien Be

166

Güncel bir cevap (Ekim 2014)

Gerçekten bu dize doğal sıralama düzeni hakkında rahatsız oldu, bu yüzden bu sorunu araştırmak için biraz zaman aldı. Umarım bu yardımcı olur.

Uzun lafın kısası

localeCompare()karakter desteği badass, sadece kullanın. Belirtildiği gibi Shog9, sorunuzun cevabı:

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

Tüm özel javascript "doğal dize sıralama düzeni" uygulamalarında bulunan hatalar

Dize karşılaştırmasını daha kesin bir şekilde "doğal dize sıralama düzeni" olarak adlandırmaya çalışan bir sürü özel uygulama var.

Bu uygulamalarla "oynarken" her zaman garip bir "doğal sıralama düzeni" seçimi, daha doğrusu hatalar (veya en iyi durumlarda eksiklikler) fark ettim.

Genellikle, özel karakterler (boşluk, tire, ve işareti, parantez vb.) Doğru şekilde işlenmez.

Daha sonra onları farklı yerlerde karışık olarak göreceksiniz, genellikle şunlar olabilir:

  • bazıları büyük 'Z' ve küçük 'a' arasında olacaktır
  • bazıları '9' ile büyük 'A' arasında olacaktır
  • bazıları küçük harf 'z'den sonra olacak

Birisi, özel karakterlerin (belki de her zaman ilk karakter olurdu) hariç, özel karakterlerin hepsinin birlikte tek bir yerde "gruplandırılmasını" beklediğinde. Yani ya sayılardan önce ya da sayılar ve harfler arasında (küçük harf ve büyük harf birbiri ardına “birlikte” olmak) ya da harflerden sonra gelir.

Sonuç olarak, nadiren sıra dışı karakterler (diakritik karakterler veya tire, ünlem işareti vb. Gibi karakterleri) eklemeye başladığımda hepsinin tutarlı bir düzen sağlayamamasıdır.

Özel uygulamalar üzerine araştırmalar:

Tarayıcıların yerel "doğal dize sıralama düzeni" uygulamaları localeCompare()

localeCompare()en eski uygulama (yerel ayarlar ve seçenekler bağımsız değişkenleri olmadan) IE6 + tarafından desteklenir, bkz. http://msdn.microsoft.com/en-us/library/ie/s4esdbwz(v=vs.94).aspx (aşağı doğru localeCompare ( ) yöntem). Yerleşik localeCompare()yöntem, uluslararası ve özel karakterleri bile sıralamada çok daha iyi bir iş çıkarır. localeCompare()Yöntemi kullanmanın tek sorunu "kullanılan yerel ayar ve sıralama düzeninin tamamen uygulamaya bağlı olmasıdır". Başka bir deyişle, stringOne.localeCompare (stringTwo) gibi localeCompare kullanırken: Firefox, Safari, Chrome ve IE, Dizeler için farklı bir sıralama düzenine sahiptir.

Tarayıcıya özgü uygulamalar üzerinde araştırmalar:

"String doğal sıralama düzeni" nin zorluğu

Sağlam bir algoritma uygulamak (yani tutarlı ama aynı zamanda çok çeşitli karakterleri kapsayan) çok zor bir iştir. UTF8, 2000'den fazla karakter içerir ve 120'den fazla komut dosyasını (dili) kapsar . Son olarak, bu görevler için bazı özellikler vardır, buna http://www.unicode.org/reports/tr10/ adresinden ulaşılabilen "Unicode Harmanlama Algoritması" denir . Bu konuda daha fazla bilgi bulabilirsiniz /software/257286/is-there-any-language-agnostic-specification-for-string-natural-sorting-order

Final sonucu

Bu nedenle, karşılaştığım javascript özel uygulamaları tarafından sağlanan mevcut destek düzeyi göz önüne alındığında, muhtemelen tüm bu karakterleri ve komut dosyalarını (dilleri) desteklemeye yaklaşan hiçbir şey görmeyeceğiz. Bu nedenle tarayıcıların yerel localeCompare () yöntemini kullanmayı tercih ederim. Evet, tarayıcılar arasında tutarlı olmamanın dezavantajı var, ancak temel testler, çok daha geniş bir karakter yelpazesini kapsadığını ve sağlam ve anlamlı sıralama düzenlerine izin verdiğini gösteriyor.

Tarafından işaret Yani olarak Shog9, sorunuzun cevabı:

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

Daha fazla okuma:

Beni "doğru" yöne getiren Shog9'un güzel cevabı sayesinde


38

Cevap (Modern ECMAScript'te)

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

Veya

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

Açıklama

Bir boole değerinin bir sayıya dökümü aşağıdakileri verir:

  • true -> 1
  • false -> 0

Üç olası modeli düşünün:

  • x, y'den büyük: (x > y) - (y < x)-> 1 - 0->1
  • x eşittir y: (x > y) - (y < x)-> 0 - 0->0
  • x, y'den küçük: (x > y) - (y < x)-> 0 - 1->-1

(Alternatif)

  • x, y'den büyük: +(x > y) || -(x < y)-> 1 || 0->1
  • x eşittir y: +(x > y) || -(x < y)-> 0 || 0->0
  • x, y'den küçük: +(x > y) || -(x < y)-> 0 || -1->-1

Dolayısıyla bu mantıklar tipik sıralama karşılaştırıcı işlevlerine eşdeğerdir.

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

1
Bu hileyi kullanan önceki cevaba yorum yaptığım gibi , sadece kod cevapları nasıl çalıştıklarını açıklayarak daha kullanışlı hale getirilebilir.
Dan Dascalescu

Ek açıklama
mpyw

Bunun localeCompare'ten daha iyi veya daha kötü olup olmadığı hakkında yorum yapabilir misiniz?
Lottem

3
@RanLottem localeCompareve standart karşılaştırma farklı sonuçlar verir. Hangisini bekliyorsun? ["A", "b", "C", "d"].sort((a, b) => a.localeCompare(b))büyük / küçük harfe duyarlı olmayan alfabetik sırada ["A", "b", "C", "d"].sort((a, b) => (a > b) - (a < b))sıralar
mpw

Görüyorum ki, bu ana bir yapışma noktası gibi görünüyor. Performans farkları hakkında bir fikriniz var mı?
Lot Lottem

13

Burada> veya <ve == kullanmalısınız. Yani çözüm şöyle olurdu:

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
Yan notta, dize ve sayı karşılaştırmaları işlenmez. Örneğin: 'Z' <9 (yanlış), 'Z'> 9 (ayrıca yanlış ??), 'Z' == 9 (ayrıca yanlış !!). Aptal NaN JavaScript'te ...
Kato


7

dizeler doğrudan javascript içinde karşılaştırılabildiğinden, bu işi yapacak

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

bir sıralama işlevindeki çıkarma yalnızca alfabetik olmayan (sayısal) sıralama istendiğinde ve elbette dizelerle çalışmadığında kullanılır


6

Bu konuda uzun süredir rahatsız olmuştum, bu yüzden sonunda bunu araştırdım ve şeylerin neden böyle olduğu için bu uzun soluklu nedeni verdim.

Gönderen 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)

Şimdi 11.9.6'ya gidiyoruz

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.

Bu kadar. Dizelere uygulanan üçlü eşittir işleci, bağımsız değişkenler tam olarak aynı dizelerse (karşılık gelen konumlarda aynı uzunluk ve aynı karakterler) true değerini döndürür.

Yani ===farklı kaynaklardan gelmiş olabilir dizeleri karşılaştırmak için çalışıyoruz durumda iş, ama sonunda aynı değerlere sahip olacaktır biliyorum hangi olacak - Bizim kodunda satır içi dizeleri için ortak yeterince senaryo. Örneğin, adında bir değişkenimiz varsa connection_stateve şu ['connecting', 'connected', 'disconnecting', 'disconnected']andaki durumlardan hangisinin şu anda olduğunu bilmek istiyorsak doğrudan ===.

Ama dahası da var. 11.9.4'ün hemen üzerinde kısa bir not var:

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. Şimdi ne var? Dışarıdan elde edilen dizeler tuhaf unicodey olabilir ve büyük olasılıkla olacaktır ve nazikimiz ===onları adalet yapmayacaktır. localeCompareKurtarmaya gelir :

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. 

Şimdi eve gidebiliriz.

tl; dr;

Javascript dizeleri karşılaştırmak için localeCompare; örneğin dahili program sabitleri oldukları için dizelerin ASCII olmayan bileşenleri olmadığını biliyorsanız, o zaman ===da çalışır.


0

İlk sorunuzdaki operasyonunuzda aşağıdaki işlemi gerçekleştiriyorsunuz:

item1.attr - item2.attr

Bu nedenle, bunların sayılar olduğunu varsayarsak (ör. İtem1.attr = "1", item2.attr = "2") Yazdığınızdan emin olmanız koşuluyla "===" operatörünü (veya diğer katı değerlendiricileri) kullanabilirsiniz. Aşağıdakiler işe yaramalıdır:

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

AlphaNumeric iseler, localCompare () kullanın.


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

Nasıl çalışır örnekleri:

+('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
Yalnızca kod yanıtları, nasıl çalıştıklarını açıklayarak daha kullanışlı hale getirilebilir.
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
Lütfen sorunun cevabı nasıl çözeceğine dair bazı bilgiler ekleyin. Yalnızca kod yanıtları kabul edilmez. Teşekkür ederim.
wayneOS

burada bir dize içindeki karakterleri sipariş etmek istersiniz, bu sorulan şey değildir. Bu sıralamayı basitçe "Array.sort" kullanarak yapabilirsiniz, örneğin 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)

Bu kod soruyu çözebilir, ancak bunun sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olabilir ve muhtemelen daha fazla oyla sonuçlanır. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın. Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
Dave
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.