Last active
February 18, 2023 12:47
-
-
Save shalvah/74a5a8aa2fc6dad5d219709f16802d48 to your computer and use it in GitHub Desktop.
Mixed multi-level order list sort https://blog.shalvah.me/posts/a-mixed-multi-level-order-list-sort-implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$orderList = [ | |
'Jake', | |
'Scully' => [ | |
'pet', | |
'friend' => [ | |
'Michael', | |
'Merissa', | |
], | |
'Norm', | |
], | |
'Amy', | |
'Charles', | |
'Hitchcock', | |
]; | |
$items = [ | |
[ | |
'name' => 'Scully', | |
'related' => [ | |
[ 'name' => 'Norm', 'category' => 'self' ], | |
[ 'name' => 'Earl', 'category' => 'friend' ], | |
[ 'name' => 'Kelly', 'category' => 'pet' ], | |
[ 'name' => 'Michael', 'category' => 'friend' ], | |
[ 'name' => 'Merissa', 'category' => 'friend' ], | |
], | |
], | |
[ | |
'name' => 'Rosa', | |
'related' => [], | |
], | |
[ | |
'name' => 'Jake', | |
'related' => [ | |
[ 'name' => 'Franzia', 'category' => 'nemesis' ], | |
[ 'name' => 'Doug' ], | |
], | |
], | |
[ | |
'name' => 'Hitchcock', | |
'related' => [], | |
], | |
[ | |
'name' => 'Amy', | |
'related' => [], | |
], | |
]; | |
function getTopLevelItemsFromMixedList(array $mixedList): array { | |
$topLevels = []; | |
foreach ($mixedList as $item => $value) { | |
$topLevels[] = is_int($item) ? $value : $item; | |
} | |
return $topLevels; | |
} | |
function sort_by(array $array, callable $sortKeyResolver, ...$sortArgs) { | |
$mapped = array_map($sortKeyResolver, $array); | |
asort($mapped, ...$sortArgs); | |
$sorted = []; | |
foreach ($mapped as $key => $index) { | |
$sorted[] = $array[$key]; | |
} | |
return $sorted; | |
} | |
$topLevelOrder = getTopLevelItemsFromMixedList($orderList); | |
$sortKeyResolver = function ($item) use ($topLevelOrder) { | |
$orderListIndex = array_search($item['name'], $topLevelOrder); | |
return $orderListIndex === false ? count($topLevelOrder).$item['name'] : $orderListIndex; | |
}; | |
// First, sort parents | |
$items = sort_by($items, $sortKeyResolver, SORT_NATURAL); | |
// Then sort children | |
foreach ($items as &$item) { | |
$children = $item['related'] ?? []; | |
if(empty($orderList[$item['name']])) { | |
// No order was specified for the children, so just sort them as normal | |
$item['related'] = sort_by($children, fn ($child) => $child['name'], SORT_NATURAL); | |
continue; | |
} | |
// An order was specified; it can contain children names or categories, | |
// so let'sjust call it the "Level 2" order (L2). | |
$l2Order = getTopLevelItemsFromMixedList($orderList[$item['name']]); | |
$sortKeyResolver = function ($childItem) use ($l2Order) { | |
// First, we check if there's an entry for the item's name | |
$indexOfChildInL2Order = array_search($childItem['name'], $l2Order); | |
if ($indexOfChildInL2Order !== false) { | |
return $indexOfChildInL2Order; | |
} | |
// Check if there's an entry for the item's category | |
$indexOfCategoryInL2Order = array_search($childItem['category'], $l2Order); | |
if ($indexOfCategoryInL2Order !== false) { | |
// There's an entry for the category! | |
// Now check if there's an entry for the item within the category. | |
$orderOfChildrenInCategory = $l2Order[$childItem['category']] ?? []; | |
$indexOfChildInCategory = array_search($childItem['name'], $orderOfChildrenInCategory); | |
return $indexOfChildInCategory === false | |
? $indexOfCategoryInL2Order.$childItem['name'] | |
: ($indexOfCategoryInL2Order + ($indexOfChildInCategory * 0.1)); | |
} | |
return count($l2Order).$childItem['name']; | |
}; | |
$item['related'] = sort_by($children, $sortKeyResolver, SORT_NATURAL); | |
} | |
print_r($items); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment