前言
在特定情境下,我們想要把 array 裡的東西同時照特定的方式做排序
其中有幾個是固定的,有幾個是隨機的
比如說我們有 itemA ~ itemF 總共六個 item
我們想要把排序第一、第三的給固定下來
同時讓排序第二、第四個從 itemA ~ itemF 給取出但不重複
所以結果可能是這樣
也可能是這樣
那要怎麼做呢?
以下就架構,分兩個部份討論
架構
一、存 db 格式
為簡化討論
我們假設 itemTable 裡只有三個欄位
分別是 id、name、sort
在存資料時
我們把要固定欄位的紀錄之 sort 設為正數
(下圖例為 1、3)
而隨機排序的資料 sort 設為 -1
這樣的好處是我們一眼就可以知道
哪些紀錄是要固定欄位、哪些又是要隨機排序的
二、排序方式
這邊先理理頭緒
用自然語言寫出接下來要怎麼處理排序
(大概就是 Pseudocode 的概念)
然後再來寫 code 會比較順
自然語言版
- 將隨機排序的
$aryWithoutOrder
資料過濾出來 (sort = -1 的資料) - 新增一個新的
$newAry
並把要固定排序的資料放進去 - 把
$newAry
裡面沒有資料的位置填滿,
填滿的方式為從$aryWithoutOrder
隨機取出
正式寫 code 版
public static function do_sortItems($items = [])
{
// 假設有 5 個 item
// 固定 itemA、itemC 為欄位 1 & 3 (sort 為 1 & 3)
// 其他的隨機排序 (設為 -1)
$items =
[
[
'name' => 'itemA',
'sort' => 1,
],
[
'name' => 'itemB',
'sort' => -1,
],
[
'name' => 'itemC',
'sort' => 3,
],
[
'name' => 'itemD',
'sort' => -1,
],
[
'name' => 'itemE',
'sort' => -1,
]
];
// 先將隨機排序的 item 取出放到 $aryWithouOrder 裡
$aryWithoutOrder = \__::filter($items, function($n) {
return $n['sort'] < 0;
});
$newAry = [];
// 最多取 8 個 item 出來
$maxLength = safeCount($items) < 8 ? safeCount($items) : 8;
// 使用 bottomline 裡的 filter function 來取出已固定 sort 的item
// 並存到 $newAry 裡
for ($i = 0; $i < $maxLength; $i++) {
$newAry[$i] = \__::filter($items, function ($n) use ($i) {
return $n['sort'] == $i + 1 ? $n['sort'] == $i + 1 : [];
});
}
// 逐一檢查 $newAry ,若 value 為空,則隨機從 $aryWithoutOrder 裡拿一個 item 出來
// 同時將該 item 從 $aryWithoutOrder 裡拿掉
foreach ($newAry as $i => $j) {
if (empty($j)) {
$randomKey = rand(0, safeCount($aryWithoutOrder) -1);
$newAry[$i] = $aryWithoutOrder[$randomKey];
$newAry[$i]['sort'] = $i+1;
$aryWithoutOrder = array_splice($aryWithoutOrder, $randomKey, 1);
}
}
return $newAry;
}
參考資料:
- 使用到的 library
bottomline: https://maciejczyzewski.github.io/bottomline/documentation/#filter