1. 程式人生 > >我可能不懂Array.prototype.sort

我可能不懂Array.prototype.sort

turn 問題 lint 完成 http digi beego fix width

今天 fix 我們後臺系統的一些 bug。系統是基於 beego 和模板開發的,各種前後端代碼揉作一團,沒有格式,沒有 eslint,全局變量滿天飛,連 js 代碼都有後端的插值,讀起來非常 酸爽

我耐著性子看了半天,陸陸續續改了幾個 bug,順便整理一下代碼,總算完成的差不多了。只剩下最後一個小問題,樂觀估計可以十分鐘內搞定。想到這裏,我不禁激動地哼起了小曲兒,馬上要從酸爽的代碼中抽身了。然而,十分鐘過去了,半個小時過去了。。

技術分享圖片

這事還真沒那麽簡單,事情要從我自以為‘熟悉’的 Array.prototype.sort 這個方法說起。

根據 MDN 的文檔,這個方法接收一個可選的 compareFunction。而這個 compareFunction 接收兩個數組元素,並返回一個值決定這兩個元素是否需要調換位置,規則如下:

如果 compareFunction(a, b) 小於 0 ,那麽 a 會被排列到 b 之前;
如果 compareFunction(a, b) 等於 0 , a 和 b 的相對位置不變。備註: ECMAScript 標準並不保證這一行為,而且也不是所有瀏覽器都會遵守(例如 Mozilla 在 2003 年之前的版本);
如果 compareFunction(a, b) 大於 0 , b 會被排列到 a 之前。
compareFunction(a, b) 必須總是對相同的輸入返回相同的比較結果,否則排序的結果將是不確定的。

知道這個規則之後,我們就可以愉快的對數組按自己規則來進行排序了,普通的升序或者降序自然沒的說,然而我面對的是這麽一個自定義規則:

有一個數組,由一位數,兩位數和 3 位數構成;現在需要排序後的數組按整體從小到大排列,但是兩位數的元素要放到最後。舉個例子:

function compare(a,b){
 // TODO
}

let arr = [1, 8, 3, 11, 100, 15, 201]

arr.sort(compare)

arr //[1, 3, 8, 100, 201, 11, 15]

到了這裏,大家可以先嘗試寫一下這個 compare 函數;如果能成功輸出正確的結果,那麽這篇文章對你也就沒什麽用了。在說答案之前,先說一個我之前理解存在的誤區:

compare 函數接收到的兩個元素在數組中的位置是不是一定 a 在前面,b 在後面?其實不是。在 compare 函數中打印出 a 和 b 就可以發現這一點。

自定義排序

知道 a 和 b 是無序的之後,我們就可以嘗試寫一下這個比較函數了,傳入的元素可以分為以下 3 種情況:

  1. a,b 都是兩位數時,按從小到大排序
  2. a,b 中有一個兩位數,兩位數放到後邊
  3. a,b 都不是兩位數,按從小到大排序

所以,代碼如下:

function isdoubleDigit(num){ return num >= 10 && num <= 99 }
function compare(a, b){
    // 都是兩位數
    if(isdoubleDigit(a) && isdoubleDigit(b)) {
      return a - b
    }
    
    // a是兩位數,b不是,a應該被放到最後
    if(isdoubleDigit(a) && !isdoubleDigit(b)) {
      return 1
    }
    
    // b是兩位數,a不是,b應該被放到最後
    if(!isdoubleDigit(a) && isdoubleDigit(b)) {
      return -1
    }
    // 都不是兩位數,正常排序
    return a - b
}

這裏說一下我原先理解中的第二個誤區,那就是以為, compareFunction是用來交換a,b元素在數組中的位置的,像冒泡排序那樣。其實這種看法是錯誤的,看下面的截圖可以看出,compareFunction只是在決定排序後的數組中a,b的相對順序,而不是對a,b的位置直接進行交換。
技術分享圖片

如MDN中所說,sort方法是用原地算法實現的,有興趣的朋友可以去研究一下,本文完。

我可能不懂Array.prototype.sort