3v4l.org

run code in 300+ PHP versions simultaneously
<?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);
Output for 8.1.10 - 8.1.28, 8.2.10 - 8.2.18, 8.3.0 - 8.3.6
Array ( [0] => Array ( [name] => Jake [related] => Array ( [0] => Array ( [name] => Doug ) [1] => Array ( [name] => Franzia [category] => nemesis ) ) ) [1] => Array ( [name] => Scully [related] => Array ( [0] => Array ( [name] => Kelly [category] => pet ) [1] => Array ( [name] => Earl [category] => friend ) [2] => Array ( [name] => Merissa [category] => friend ) [3] => Array ( [name] => Michael [category] => friend ) [4] => Array ( [name] => Norm [category] => self ) ) ) [2] => Array ( [name] => Amy [related] => Array ( ) ) [3] => Array ( [name] => Hitchcock [related] => Array ( ) ) [4] => Array ( [name] => Rosa [related] => Array ( ) ) )

preferences:
65.77 ms | 406 KiB | 28 Q