專案裡有個 AngularJS 畫面,scope.datalist 每筆資料的欄位開頭儲存著 (一)(二)(十)(二十) 這類中文數字前綴。Array.sort 預設按 Unicode 碼位排,(一) 排在 (三) 前面沒問題,但 (十)(二十) 的字元碼比個位數字大,順序就全亂了。

下面這個 customSort 把中文數字轉成阿拉伯數字再比較,支援一到九十九,沒有前綴的項目自動排到最後。

欄位名稱說明: 範例中的 Catacory 是原始資料庫欄位名稱(歷史拼錯沿用至今),不是 typo 建議,直接抄用時請換成你自己的欄位名。

完整程式碼

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 getNumber = (str) => {
if (!str) return 100; // null / undefined / 空字串全部排到最後
const match = str.match(/^\(([一二三四五六七八九十]+)\)/);
if (!match) return 100;

const numStr = match[1];
const numMap = { 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, 七: 7, 八: 8, 九: 9, 十: 10 };

if (numStr.length === 1) {
// 「一」到「九」或「十」
return numMap[numStr] || 0;
} else if (numStr.startsWith('十')) {
// 「十」= 10,「十一」到「十九」= 11–19
// numStr.length === 1 的「十」已在上面處理,到這裡 length >= 2
return numStr.length === 2 ? 10 + numMap[numStr[1]] : 10;
} else {
// 「二十」到「九十九」:首位 * 10 + 末位(「二十」沒有末位,numMap[undefined] || 0 = 0)
return numMap[numStr[0]] * 10 + (numMap[numStr[2]] || 0);
}
};

function customSort(a, b) {
const aNum = getNumber(a.Catacory);
const bNum = getNumber(b.Catacory);
return aNum - bNum;
}

scope.datalist.sort(customSort);

邏輯拆解

正則抓取前綴

1
str.match(/^\(([一二三四五六七八九十]+)\)/)

^ 確保從字串開頭比對;\( \) 匹配全形括號的半形版本(即 ASCII 括號);中間的捕獲群組取出純漢字部分,丟給後面的轉換邏輯。

中文數字轉阿拉伯數字

三條路徑,覆蓋一到九十九:

輸入 路徑 計算 結果
一、二…九 長度 1 numMap[numStr] 1–9
長度 1 → numMap['十'] numMap['十'] = 10 10
十一…十九 以「十」開頭,長度 2 10 + numMap[numStr[1]] 11–19
二十…九十 其他,長度 2 numMap[numStr[0]] * 10 + 0 20–90
二十一…九十九 其他,長度 3 numMap[numStr[0]] * 10 + numMap[numStr[2]] 21–99

注意 numStr[2] 取的是第三個字元(索引從 0 起算),「二十一」的索引 2 剛好是「一」,正確。若欄位有超過九十九的情況,這個函式無法處理,需要另外擴充。

null / undefined 防禦

原版沒有防禦,str.match() 收到 nullundefined 直接拋 TypeError。加一行:

1
if (!str) return 100;

讓缺值資料和無前綴資料一樣排到最後,不會 crash。

無前綴項目排到最後

1
if (!match) return 100;

100 大於所有可能的中文數字轉換結果(最大 99),確保沒有括號前綴的資料落在排序尾端。

回傳 0 與 AngularJS / IE11

aNum === bNum 時回傳 0,依賴排序演算法的穩定性維持原始相對順序。ES2019 之後 JavaScript 規範明確要求穩定排序,現代瀏覽器和 Node.js 11+ 都符合。

不過 scope.datalist 暗示 AngularJS 場景,若仍需支援 IE11,IE11 的 Array.sort 不保證穩定。這種情況要麼在 return 0 前加索引比較當 tie-breaker,要麼確認已脫離 IE11 支援需求再沿用現有寫法。