Skip to content

Instantly share code, notes, and snippets.

@shalvah
Created Sep 24, 2022
Embed
What would you like to do?
Mixed multi-level order list sort
<?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