js程式碼sort排序的bug亂序解決辦法
【現象】
程式碼如下:
var list = [{ n: "a", v: 1 }, { n: "b", v: 1 }, { n: "c", v: 1 }, { n: "d", v: 1 }, { n: "e", v: 1 }, { n: "f", v: 1 }, { n: "g", v: 1 }, { n: "h", v: 1 }, { n: "i", v: 1 }, { n: "j", v: 1 }, { n: "k", v: 1 }, ]; list.sort(function (a, b) { return a.v - b.v; }); for (var i = 0; i < list.length; i++) { console.log(list[i].n); }
很簡單,就是定一個物件陣列,再用sort方法按v欄位對其排序,這個問題很容易讓人忽略,因為按正常思維都會認為只要所有v是相等的那麼結果就跟沒排序之前是一樣的,但是……
結果如下:
====IE11====
====火狐====
====Chrome====
可以看出,IE跟火狐都沒問題,但Chrome卻成了亂序。
經過查閱資料,網上對這個問題似乎沒有多少實際的解決辦法。據說谷歌開發者認為這不是個bug不予解決,因為V8引擎的原因,為了高效排序,稱之為不穩定排序。其實這也不算是一個BUG,不同人會有不同的看法:a:"因為排序依據是相同的就是沒有順序,沒有順序就是亂序,這種結果是正確的";b:"既然排序依據是相同的那就按照原始順序輸出"(這應該是大多資料語言裡常規的做法)。網上有牛人說陣列超過10條後會呼叫另一種排序方法(插入排序),10以下用的是快速排序演算法,為了提交效率,所以會出現這種情況。
網上有人給出了辦法就是相同的情況下強制產生差異(當順序相同時,讓a比b小):
list.sort(function (a, b) {
return a.v - b.v || -1;
});
經測試,該方法無效。原因可能是返回值都相同導致。
很苦惱,於是繼續想辦法,既然返回值相同也會出現這個問題,那如何能讓返回值即不相同排序結果還要正確呢?於是經過一番思索,想到了一個值:index,沒錯!就是根據索引來排序,如果順序相同那麼就根據比較索引,索引的順序就是排序前的順序,於是程式碼改為:
list.sort(function (a, b) { return a.v - b.v || list.indexOf(a)-list.indexOf(b); });
測試結果仍然不行。
有些苦惱,仔細想了一下發現:思路是沒有問題,但是在排序中元素位置是不斷髮生變化的,所以indexOf取出的不是原始的索引位置,所以排序仍然不正確。因此按照這個思路去做,在每個元素里加個屬性來保留它原始的索引,然後再按此索引排序,程式碼更改後如下:
for (var i = 0; i < list.length; i++) {
list[i].oldIndex = i;
}
list.sort(function (a, b) {
return a.v - b.v || a.oldIndex - b.oldIndex;
});
測試結果沒問題,跟我們預期的相同!
【解決辦法】
於是正確結果就是:先迴圈給每個元素增加一個屬性,用來儲存它目前的位置,然後再排序中遇到等序時取索引進行排序
例如:
for (var i = 0; i < list.length; i++) {
list[i].oldIndex = i;
}
list.sort(function (a, b) {
return a.v - b.v || a.oldIndex - b.oldIndex;
});