單一條件排序
先來看一下 sort 最基本的用法:
const array = [1, 3, 8, 6, 4];
array.sort();
// array 會變成 [1, 3, 4, 6, 8]
單純使用 sort() 會以小到大排序,不過他是以 Unicode 編碼的方式去比,並不是以數字大小排序,也就是:
const array = [1, 100, 98, 654, 45];
array.sort();
// array 會變成 [1, 100, 45, 654, 98]
似乎跟真正需要的不一樣對吧?所以要把 sort() 改為:
const array = [1, 100, 98, 654, 45];
array.sort((x,y)=>x-y);
// array 會變成 [1, 45, 98, 100, 654]
x 和 y 是參數,可以自己命名。背後的原理是把 x y 拿出來比較,回傳索引值:
function compare(x, y) {
if (在某排序標準下 x 小於 y) {
return -1;
}
if (在某排序標準下 x 大於 y) {
return 1;
}
// x 必須等於 y
return 0;
}
sort 也可以用陣列裡的物件屬性來比較,舉例來說:
const array = [
{title:'pineapple', price: 100},
{title:'banana', price: 35}
];
array.sort((x,y)=>x.price-y.price);
// array 會變成 [
{title: 'banana', price: 35},
{title: 'pineapple', price: 100}
]
比較文字時因為會牽扯到語系問題,舉例來說英文要以英文字母來排、中文要以筆畫來排等等,可以使用 localeCompare 來解決這個問題:
// 英文const array = ['pineapple', 'banana'];
array.sort((x,y)=>x.localeCompare(y));
// array 會變成 ['banana', 'pineapple']// 中文
const array = ['鳳梨', '香蕉'];
array.sort((x,y)=>x.localeCompare(y, 'zh-Hant'));
// array 會變成 ['香蕉', '鳳梨']
多重條件排序
如果要比較陣列裡的物件,先以物件的價格做排序,價格一樣時以標題做排序,該怎麼處理呢?只要搭配 || 就可以了。
const array = [
{title:'pineapple', price: 100},
{title:'banana', price: 35},
{title:'apple', price: 35}
];
array.sort((x,y)=>x.price-y.price || x.title.localeCompare(y.title));
// array 會變成 [
{title: 'apple', price: 35},
{title: 'banana', price: 35},
{title: 'pineapple', price: 100}
]
問題
講了這麼多,終於可以講我遇到的問題。這個問題的需求是:
- 取得 API 後,需要先以攤位編號(booth_number)排序
- 若攤位編號相同,以標題(title)排序
- 標題可能中英文混雜,以第一個字的英文字母、中文筆畫順序排序
- 攤位編號後台可能沒有輸內容,也就是抓到的資料為空值 null 。因此若無攤位邊號,要放在最後面,以標題第一個字的英文字母、中文筆畫順序排序
直覺上我就想,
- 先把有攤位編號資料的東西抓出來變成一個陣列 boothNotNull、沒有的變成另一個陣列 boothNull。
- 把 boothNull 裡先抽出第一個字母,把他們詳細區分成中文陣列、英文陣列,分開排序,再把排序後的內容放回一起。
- boothNotNull 則是先比較攤位編號,然後…
然後就卡關了。雖然我知道可以用 || 來做多重判斷,但裡頭還要額外判斷中文、英文,這些又要用不同語系排序…
解決
後來轉念一想,我不如:
- 先不管有沒有攤位編號,把標題中英文排序搞定。
- 取得所有資料的中英文排序後,用 indexOf 找到他在正確排序陣列中的位置,在每筆物件中新增一個 titleKey 屬性存放。
- 接著開始區分攤位編號,有資料的放一組,沒資料的放一組。
- 有資料的以編號排序,編號相同時以 titleKey 比較。
- 無資料時以 titleKey 比較。
以上就是這次的解決方案!