跳至主要内容

Javascript sort()

特性

1. 原地算法 in place -> 會覆蓋掉原輸入值

資訊

在 ECMAScript 2023 提出了 toSorted(),不確定是否已經是 release stable。作用跟 sort 一樣用來排序,差別在於會回傳一個新的 Array。語法基本上與 sort 的使用沒區別,之後會再補充 toSorted 的理解。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted

2. 預設的排序順序是根據字串的 Unicode 編碼位置(code points)而定。

3. 排序出的結果為 unstable 不穩定的

資訊

sort 在 ECMAScript 2019(也稱為 ES10)前是不穩定,也就是 compare = 0 時,仍有可能順序改變,但在 ES10 後,則是穩定不變的。
也得看瀏覽器的版本是否支援 ES10,總之,在目前的瀏覽器環境多元宇宙下,建議還是把排序比較的定義函式寫得更為細節。
https://exploringjs.com/es2018-es2019/ch_array-prototype-sort-stable.html

let people = [
{ firstName: "John", lastName: "Doe" },
{ firstName: "Jane", lastName: "Doe" },
{ firstName: "Alice", lastName: "Smith" },
{ firstName: "Bob", lastName: "Smith" }
];

例如 John 跟 Jane 的 lastName 都是 Doe,在排序時會拿到 compare = 0,也就是照理說不應該變動順序。
但在不穩定的排序算法中,是有可能變成 Jane 在 John 之前的。

使用

arr.sort()
arr.sort([compareFunction])

-- 可以不帶比較函式,不帶的話就根據 JS 定義的方式排序,就是特性 2 提到的編碼位置。

sort number

要特別注意的是數字的排序,會跟我們常理預期的有落差,例如下方的例子:我們會預期 10 在 3 之後,但實際不然。

[3, 10, 1].sort() // [1, 10, 3]

這是因為在 JS 的 UTF-16 比較中,數字在比較前會先轉變為 string type。而在 string 的比較方式裡,"10" 裡面帶有 "1",這個 "1" 的 Unicode code 比 "3" 更小,因此直接判定 "10" 在 "3" 前。 要走到比較 "10" 的第二個字符 "0",前提是第一個字符必須相等,例如 "10" 跟 "11"。

[3, 10, 1].sort() -> ['3', '10', '1'].sort()
// Unicode code "1" is 49.
// Unicode code "3" is 51.

[3, 12, 11].sort() -> ['3', '12', '11'].sort() // return [11, 12, 3]
// 會比較到 '12' 的 '2' 以及 '11' 的 '1'

因此大部分情況,當我們需要比較數字時,我們會使用 compareFunction!(下方就會說明)

sort string

字串的排序就稍微合乎常理一點,而且跟數字一樣會先比較第一個字符,相同才比較第二個。但要注意以下
-- 大小寫:所有大小字母比小寫字母更前
-- 地區:某些地區的字母排序可能與 Unicode 不同 -> 可研究 localeCompare、Intl.Collator
-- 特殊符號:標點符號、空格等等

['b','a'].sort() // ['a', 'b']
['B','a'].sort() // ['B', 'a']

// Unicode code "A" is 65.
// Unicode code "a" is 97.

// 如果需要對字符串進行更複雜的排序(如忽略大小寫、按地區特定規則排序等),可以自定義排序函數或使用 JS 提供的 method
['B', 'a', 'C', 'b'].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
// ['a', 'B', 'b', 'C']
// 這例子也是使用了 compareFunction

compareFunction 比較函式

前面提到了這麼多次 compareFunction,終於可以來看看是什麼東西。
總結來說,「可以用一個函式來定義排序順序,比較函式必須回傳正負值或者 0 ,讓 sort 去排順序」。 -- compareFunc(a,b) return value < 0,a 在 b 前
-- compareFunc(a,b) return value > 0, b 在 a 前
-- compareFunc(a,b) return value = 0,理論上不變(不清楚的話,可以回顧上面提到的 unstable)

// 原理
function compare(a, b) {
if (在某排序標準下 a 小於 b) {
return -1;
}
if (在某排序標準下 a 大於 b) {
return 1;
}
// a 必須等於 b
return 0;
}

// example
// 需求:產品照 credit 是否有值排列,有值的先放前面,但沒有要比較大小
export const sortProductsBySubscribe = (products: Interface[]) =>
[...products].sort((a, b) => {
if (a.price.credit === null) return 1;
if (b.price.credit === null) return -1;
return 1;
});

/** 這個 compareFunction 會比較 product 的 credit
* 若當前的產品 a 的 credit 為 null,下一個 b 會排在 a 前
* 若下一個產品 b 的 credit 為 null,a 會排在 b 前
* 若都不為 null (都有值),預設當前 a 會排在下一個 b 前
*/
參考

https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/sort