JavaScript數組排序詳解


文章出自個人博客https://knightyun.github.io/2020/01/12/js-array-sort,轉載請申明


提到 JavaScript 中對數組進行排序操作,可能首先想到的就是 Array.prototype.sort() 這個函數,比如以下場景就比較常見:

var arr = [3, 1, 2];

console.log(arr.sort());
// [1, 2, 3]
console.log(arr); // sort() 函數會修改原數組
// [1, 2, 3]

arr = ['c', 'b','B', 'a','A'];
arr.sort();
console.log(arr);
// ["A", "B", "a", "b", "c"]

和預想的一樣,sort() 函數默認將數組元素升序排列,但是不要被上面的數字數組的排序結果迷惑,該函數並不是按照數字遞增的方式排列的,而是按照元素的 ASCII 碼或者 Unicode 碼進行排序,比如字符 a 對應的 ASCII 碼要比字符 b 的小,所以 a 排在 b 前面,同樣字符 A 的比字符 a 的小,所以大寫字母 A 會排在小寫字母 a 前面;考慮以下情景:

var arr = [1, 2, 11, 12];
arr.sort();
console.log(arr);
// [1, 11, 12, 2]

是不是有些和預想的不一樣,這也驗證了之前所說,並不是按照數字遞增在排序,而是把數組中的數字類型的元素轉換成字符,在拆分字符比較單個字符對應的字符碼的大小;

比較函數

那麼問題就來了,要按照數字遞增方式排序,該怎麼操作呢?其實這種情況早就被 .sort() 函數考慮到了,只是可能被大家忽略了,就是 .sort() 函數還能接受一個參數,叫做 compareFunction,顧名思義,就是 比較函數,由於該參數是一個函數,所以該函數又能接受兩個參數,即比較的值,所以最終就是 .sort(compareFunction(a, b))

關於這個 比較函數,存在如下規則:

  • 如果 compareFunction(a, b) 返回值小於 0 ,那麼 a 會被排列到 b 之前;
  • 如果 compareFunction(a, b) 返回值等於 0 ,那麼 ab 的相對位置不變;
    • 備註: ECMAScript 標準並不保證這一行爲,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003 年之前的版本);
  • 如果 compareFunction(a, b) 返回值大於 0 ,那麼 b 會被排列到 a 之前;

compareFunction(a, b) 必須總是對相同的輸入返回相同的比較結果,否則排序的結果將是不確定的。

在使用它之前,先來看看函數裏面的參數 a, b 是如何對應數組元素的:

var arr = [2, 1, 4, 3];
arr.sort(function(a, b) {
    console.log(a, b);
})

// 1 2
// 4 1
// 3 4

可以發現,由於這裏的比較函數沒有返回值,所以對數組就沒有排序操作,而每一次遍歷中,第二個參數 b 對應前一個元素,第一個參數 a 對應後一個元素;當然該函數的具體排序方法就不得而知並且因 JS 引擎而異了;

升序

對數組按照升序方式排序,即小的元素排在前面,大的元素排在後面,假設比較函數當前遍歷的元素對爲 (2, 1),則 a = 1, b = 2,要想升序就要 a 排到 b 的前面,對應上面的規則,就是需要比較函數的返回值小於 0,由於當前 a - b < 0;所以直接返回一個 a - b 就行了,代碼如下:

var arr = [2, 1, 3, 11, 12, 11]
arr.sort(function(a, b) {
    return a - b;
})

console.log(arr);
// [1, 2, 3, 11, 11, 12]

針對上面的代碼再來分析下,在每一次遍歷比較的兩個元素中:

  • 如果後一個元素比前一個元素小,即 a - b < 0,按照規則就是 a 要排到 b 的前面,也就是這兩個元素會交換,小的在前,大的在後;
  • 如果後一個元素比前一個元素大,即 a - b > 0,按照規則就是 b 要排到 a 的前面,由於 b 本來就在 a 的前面,所以兩元素位置不變;
  • 如果後一個元素與前一個元素相同,即 a - b = 0,按照規則就是 ab 的位置不變,兩元素位置同樣不變;

最後,數組就變成升序的了;

降序

原理和升序類似,只是思路反過來了,代碼如下:

var arr = [2, 1, 3, 11, 12, 11]
arr.sort(function(a, b) {
    return b - a;
})

console.log(arr);
// [12, 11, 11, 3, 2, 1]

同樣來分析一下,每一次遍歷中:

  • 如果前一個元素比後一個元素小,即 b - a < 0,按照規則就是 a 要排到 b 的前面,也就是這兩個元素會交換,大的在前,小的在後;
  • 如果前一個元素比後一個元素大,即 b - a > 0,按照規則就是 b 要排到 a 的前面,由於 b 本來就在 a 的前面,所以兩元素位置不變;
  • 如果前一個元素與後一個元素相同,即 b - a = 0,按照規則就是 ab 的位置不變,兩元素位置同樣不變;

最後,數組也就變成降序了;

反序

這是排序函數一個另一個應用,作用相當於 .reverse() 函數,即讓數組中的元素順序顛倒,實現也很簡單,就是利用規則,讓每次比較函數的返回值小於 0 就行了,例如:

var arr = [2, 1, 4, 3];
arr.sort(function(a, b) {
    return -1
});

console.log(arr);
// [3, 4, 1, 2]

亂序

這也算是一個比較實用的用途了,即將數組中元素的位置和順序打亂,增加隨機性,實現也簡單,即利用規則,讓比較函數的返回值隨機爲 > 0, < 0, = 0 這三種情況之一,使得元素是否交換位置具有隨機性,也就實現了順序的打亂,實現代碼如下:

var arr = [1, 2, 3, 4, 5];
arr.sort((a, b) => {
    return 0.5 - Math.random();
});

console.log(arr);
// [4, 3, 2, 1, 5]

arr.sort((a, b) => {
    return 0.5 - Math.random();
});

console.log(arr);
// [5, 3, 1, 2, 4]

技術文章推送
手機、電腦實用軟件分享
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章