4.2 數組排序
PHP里的數組實際上是一個有序映射。不管是隊列、數組、棧還是字典,在使用PHP編程時,你都可以統一使用PHP的數組類型。這一節,我們只探討關于PHP數組的排序,因為數組的排序是很多項目都會使用到的。雖然數據庫也可以很方便進行排序,但在復雜、動態的業務規則下,更多需要在PHP代碼層進行處理。因此加深對PHP數組的理解,對項目開發將大有禆益。
4.2.1 一道面試題引發的思考
在我當前任職的公司里,有這樣一道面試題,關于數組排序的。簡化和提煉一下,題目是:
請按以下規則,對專輯的歌曲進行排序。
1、按播放次數,從高到低排序。
2、如果播放次數相同,則按收藏人數從高到低排序。
3、如果收藏人數相同,則按下載次數從高到低排序。
并假設有專輯歌曲列表如下:
表4-1 歌曲列表


通常,對于這道題,會有三類答案。
第一類,就是一開始就扎進大學時代的排序算法。想著從頭到尾再重新實現一套冒泡排序算法,或者快速排序算法。如果能達到性能最優,并且能準確無誤快速實現也是不錯的做法。但這更多是偏理論化,實際業務需求開發中,很少需要自己再重新實現通用的排序算法。相反,我們只需要能加以使用即可。
所以,第二類答案是,通過最少的代碼,快速實現業務需求。不管你是使用封裝好的第三方開源類庫,還是PHP原生態提供的數組排序函數,只要能實現以上排序規則即可。這時,可以使用array_multisort()函數。這個排序函數功能比較強大,因此理解起來會有點吃力。根據官方文檔的說明,array_multisort() 可以用來一次性對多個數組進行排序,或者根據某一維度或多個維度對多維數組進行排序。
例如,這里,可以這樣實現:
<?php
// 示例數據
$songs = array(
array('title' => '恰似你的溫柔', 'play' => 1000, 'like' => 900, 'download' => 800),
array('title' => '丁香花', 'play' => 2000, 'like' => 700, 'download' => 500),
array('title' => '突然的自我', 'play' => 2000, 'like' => 800, 'download' => 1300),
array('title' => '夜空中最亮的星', 'play' => 800, 'like' => 1000, 'download' => 700),);// 初始化輔助數據
$playTimes = $likeTimes = $downloadTimes = array();foreach ($songs as $it) {
$playTimes[] = $it['play'];
$likeTimes[] = $it['like'];
$downloadTimes[] = $it['download'];}// 用一行代碼,根據多維對多維數組進行排序
array_multisort(
$playTimes, SORT_DESC, SORT_NUMERIC, // 排序規則1
$likeTimes, SORT_DESC, SORT_NUMERIC, // 排序規則2
$downloadTimes, SORT_DESC, SORT_NUMERIC, // 排序規則3
$songs
);
print_r($songs);
最后,運行上面代碼,可以看到結果輸出是:
Array(
[0] => Array
(
[title]
=> 突然的自我
[play]
=> 2000
[like]
=> 800
[download]
=> 1300 ) [1] => Array (
[title]
=> 丁香花 …… ) [2] => Array (
[title]
=> 恰似你的溫柔 …… ) [3] => Array (
[title]
=> 夜空中最亮的星 …… ))
除開前面的示例數據,中間只需要簡單循環一遍,用于初始化輔助的數據,就可以便捷地實現了上述三個規則的排序。但這還不是最優的。我們再來看下第三類答案。
在揭曉第三類答案之前,我們不妨先簡單來回顧一下以前在中學時代,當遇到求解一元二次(或更高次方)方程式時,當時我們是怎么解決的?例如這一條方程式:


很簡單,我們會先化簡,再求解。即先化簡為我們熟悉的一元一次方程:


兩邊求平方根,得到:


所以,最后答案是x=3或x=-1。這樣是不是很簡單了?
同樣的道理,如果是對于三維的排序我們很陌生,或者說無從下手,那么如果這只是一道一維數組的排序呢,我們是不是可以很簡單地處理?
這就是我們所說的,第三類答案——思路最簡單的解決方案。這也是通常所說的降維。有了新的思路,再來解決就不難矣。關鍵點在于,我們要找到一種唯一映射,使得:


然后再按照此映射規則,將三維的比較,降為一維的比較,最后再進行數組排序,就能達到同樣的效果。以下是鑒于當前示例數據的參考實現。
<?php
// 三維降一維
$points = array();foreach ($songs as $it) {
$points[] = 1000000 * $it['play'] + 1000 * $it['like'] + $it['download'];}// 再排序
array_multisort($points,?SORT_DESC,?SORT_NUMERIC,?$songs);
4.2.2 對數組排序的理解
關于PHP數組的排序函數有好幾個,但通常開發同學只記得sort(),ksort(),更多其他的排序函數就記不住,或者沒有印象了。下面將分享如何快速記住這些排序函數的技巧。
從官方文檔摘錄的,對數組排序的函數有:
- array_multisort()
- asort()
- arsort()
- krsort()
- ksort()
- natcasesort()
- natsort()
- rsort()
- shuffle()
- sort()
- uasort()
- uksort()
- usort()
全部列出來,有13個之多。那怎么記得住呢?其實,在理科里,都是有技巧的,要靠理解,而非死記硬背。就像數學公式一樣,要活學活用。
我們都知道,PHP數組由鍵和值組成,而排序順序可以是升序,或者是降序。根據這兩個維度,我們可以將上面13個排序函數進行分類。首先分為兩大類:對值排序,和對鍵排序。如下面表格所示。
表4-2 數組排序函數
升序
降序
自定義
自然排序
其他
對值排序
sort()、asort()
rsort()、arsort()
usort()、uasort()
natsort()、natcasesort()
shuffle()、array_multisort()
對鍵排序
ksort()
krsort()
uksort()
對這樣的分類清晰后,接著下再來看怎么記住這結函數名稱。可以發現,除了隨機排序函數的名稱為shuffle()外,其他全部排序函數都是以“sort()”結尾的。
不難發現,函數名稱中的這些字母表示的意思分別是:
- k:表示鍵,key的縮寫
- a:表示鍵值關聯的保持(數字類型的不保持),associative的縮寫
- r:表示逆向或者翻轉,reverse的縮寫
- u:表示用戶自定義,user的縮寫
然后,再從最原始的sort()函數開始,若加上首字母a則表示保持索引關系的排序,若加上首字母k則表示對鍵排序。由此構成第一梯度排序函數:
- sort()、asort()、ksort()
這三個排序函數,若全部在“sort”前加上字母r,則表示降序排序。從而構成第二梯度排序函數:
- rsort()、arsort()、krsort()
如果在第一梯度的三個排序函數最前面加上首字母u,則更簡單了。直接表示用戶自定義排序系列的函數,即第三梯度排序函數:
- usort()、uasort()、uksort()
最后,剩下的4個函數排序可歸為第四梯度排序函數,即綜合型的排序,有:
- natsort()、natcasesort()、shuffle()、array_multisort()
通過這樣的整理,估計你能在更短的時間內,對PHP的數組排序函數有更深刻的理解。甚至乎,可以逐漸明白設計PHP這門語言當時的初衷是多么的巧妙。
版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。