0%

JS 將陣列 Array 重新排列的 sort()

Day 22

一堆樸克牌攤在眼前,要照花色排還是大小來排?隨你高興。

sort()是個很好用的陣列方法,凡是要把陣列裡面的元素,照我們想排列的方式重新排列都需要用到它。例如在購物網站的商品依價格排列,或是我們看文章時,依照最近的時間排列,都會用到sort()

在做排序時,我們也會同時原地(in place)的更動到原陣列,且回傳給我們排好序的陣列。

Array.prototype.sort() - JavaScript | MDN

原型: Array.prototype.sort()
功能: 依據字串的 Unicode 編碼進行排序。
改變: 會改變原本的陣列
語法: arr.sort([compareFunction])
回傳值: 回傳排序後的陣列
參數: 可指定一個函式來定義排序的順序。如果省略此參數就會依據字串的 Unicode 編碼進行排序。

是依據什麼來排列?

sort()是如何依照我們想要的方式排序?如果我們不給sort()參數,它會依照預設的方式排序。我們也可以給一個函式當參數,並將條件寫在函式裡,這樣會比沒有給參數精準很多。

預設排序大亂入

預設排序也就是依照我們陣列裡元素的 code UTF-16 的順序排列,什麼是 code UTF-16? UTF-16 是 Unicode 字元的一種,我們可以直接看看這張 Unicode 的編碼表,並且來做一些測試:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const arr1 = [
null,
2,
8,
undefined,
NaN,
200,
'Hi',
'apple',
'',
'#',
'!',
'2'
];
arr1.sort();

arr1;
//[ '', '!', '#', '2', 2, 200, 8, 'Hi', NaN, 'apple', null, undefined ]

排序的結果頗讓人驚訝 簡直像沒排序sort()會將所有元素轉成字串後,且以第一個字元為對象,再進行排序,而所有符號會被先篩選出來,接著是數字,然後是字母。也因為這樣的比較,所以連200都會排在8前面。而字母則以字母大寫為先、小寫為後來排序。

需要特別注意的是,這樣的結果有可能因sort()執行環境 也就是瀏覽器的不同,而有預期之外的預設排序結果。所以一般會建議,還是以函式傳入參數來當排序條件會比較穩定。

用匿名函式當排序條件

如果希望完全依照自己給的條件排序,最好是給sort()一個帶有條件的匿名函式來當參數,這個匿名函數必須要有兩個參數,然後再依照這兩個參數比較回傳的值,來當排序依據。

sort()會依匿名函式的參數與回傳的值為精確的排序規則:

  • 當回傳值為負數時,那麼前面的數放在前面
  • 當回傳值為正整數,那麼後面的數在前面
  • 當回傳值為零,保持不動。

這個函式會每次都先拿兩組陣列裡的元素來比較,當回傳值為正,會讓後面的數跑到前面,以上述的規則來移動元素,大家有沒覺得這種方式很眼熟,其實就是使用冒泡排序法來達到排序。

我們先來看這些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 沒有給參數的預設排序
const arr = [5, 9, 1, 3, 2, 6];
arr.sort(); // [1, 2, 3, 5, 6, 9]

// 以匿名函式回參數做「升序」排序
arr.sort(function(a, b) {
return a - b; // a - b > 0
});
// [1, 2, 3, 5, 6, 9]

// 如果要反過來做「降序」排序
arr.sort(function(a, b) {
return b - a;
});
// [9, 6, 5, 3, 2, 1]

或許大家會覺得疑惑,怎麼回傳一個 a - b就變升序 b - a就成了降序?
這是被精簡化的寫法 聽說高手都會越寫越精簡 ,我們可以來看看原本的寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const arr = [5, 9, 1, 3, 2, 6];
// 升序
arr.sort(function(a, b) {
if (a > b) {
return 1; // 正數時,後面的數放在前面
} else {
return -1; // 負數時,前面的數放在前面
}
});

// 降序
arr.sort(function(a, b) {
if (a < b) {
return 1; // 正數時,後面的數放在前面
} else {
return -1; // 負數時,前面的數放在前面
}
});

// 升序 另一種寫法,就會精簡到像最上面那樣!
arr.sort(function(a, b) {
if (a > b) {
// a > b 等於 a - b > 0
return a - b;
} else {
return a - b;
}
});

這就是程式好玩的地方,一步步的精簡到回傳a - b就是升、b - a就是降,還真是新手想不出來的境界啊!

可以拿 sort 來做什麼?

sort()可以運用的範圍很廣,尤其是參數的匿名函式,依照下的條件不同而達到我們想要的效果。來看看一些範例:

按照字串長度來排序

1
2
3
4
5
6
const arr = ['hi', 'Hello', 'Bonjour', 'ciao'];
arr.sort(function(a, b) {
return a.length - b.length;
});

// ["hi", "ciao", "Hello", "Bonjour"]

對字串做不區分大小寫的排序

我們在最開頭的時候有聊到,sort()的預設會以 Unicode 來排序,而在 Unicode 的排序裡,大寫字母又會比小寫前面,但當我們要對有大小寫混雜的字串陣列做排序時,如何做到不區分大小寫的排序?可參考下列的方式,先把字串全轉成小寫再比對,就可以得到不分大小寫的排序囉。

1
2
3
4
5
6
7
8
9
10
11
12
13
arr = ['apple', 'Banana', 'coco', 'Ege'];
// 直接預設排序的結果
arr.sort(); //  ["Banana", "Ege", "apple", "coco"]

// 先將字串全轉成小寫再排序
arr.sort(function(x, y) {
let a = x.toLowerCase();
let b = y.toLowerCase();
if (a > b) return 1;
if (a < b) return -1;
else return 0;
});
// ["apple", "Banana", "coco", "Ege"]

今天就先介紹到這裡,其實sort()的學問很大,這其中牽涉到的不只是演算法,還有 JavaScript 裡的匿名函式的活用,本人才疏且學淺,好多知識都還來不及吸收,以致不敢介紹給大家,期待有朝一日的自己,可以回頭講解得更清楚些。

如有需要改進的地方,拜託懇求請告知,我會盡量快速度修改,感謝您~