I’m receiving an array from a database – I have no control over what pieces of data are sent and in what order. This is what it currently looks like:
Array
(
[itemCode] => Array
(
[0] => Array
(
[code] => P
[descShort] => Pepperoni
)
[1] => Array
(
[code] => G
[descShort] => Green Peppers
)
[2] => Array
(
[code] => n
[descShort] => No Sauce
)
[3] => Array
(
[code] => x
[descShort] => No Cheese
)
[4] => Array
(
[code] =>
[descShort] => Regular Cheese
)
[5] => Array
(
[code] =>
[descShort] => Regular Sauce
)
)
)
In actual practice, there can be any number of elements before the No Sauce option (currently at index 3, but not always that way.) What the client wants is for the Cheese and Sauce items to always be at the end of the list and ordered this way: Regular Cheese, No Cheese, Regular Sauce, No Sauce.
Again, keeping in mind that I have no control over how the array is initially created, and that there could be any number of other elements both before and between the elements in question, how can I make this happen? Something else to worry about is that at some point, there may be other options they want to include in this re-ordering (they may add an option for Extra Cheese and Extra Sauce, for example, and want them to be in specific positions as well.)
Added var_export
array (
'itemCode' =>
array (
0 =>
array (
'code' => 'P',
'descShort' => 'Pepperoni',
),
1 =>
array (
'code' => 'G',
'descShort' => 'Green Peppers',
),
2 =>
array (
'code' => 'n',
'descShort' => 'No Sauce',
),
3 =>
array (
'code' => 'x',
'descShort' => 'No Cheese',
),
4 =>
array (
'code' => '',
'descShort' => 'Regular Cheese',
),
5 =>
array (
'code' => '',
'descShort' => 'Regular Sauce',
),
),
)
As it has been already commented, the
usortfunction can be used for that. But this is only the start as it needs the compare function.That is also not that hard, because we can just create one. However, it’s crucial to understand how it works:
Okay, that’s not a blocker but turns the actual question, how to find out which one is above which one? And how to do that extensible?
There are two type of values: Either the ones that do not need any ordering or the ones that need ordering. Let’s first define an array for those that need ordering. And to make it clear for what that ordering is, name the key:
Now inside a compare function, you can look up if for the A/B values an entry exists within the ordering. If it does not exists, then this needs no ordering. If it exists, this needs ordering.
These two cases are extended because you also can have an existing sort order with a non-existing one. So there are four cases to cover:
Thanks to function support in PHP, we can pass along the order-key and the ordering values into the comparison function easily for a quick example. The function then only has to do what has been outlined for the four cases.
The example:
So now there is one caveat:
usortis not stable. That means, when returning 0, items do not stay at their position. You can work around that by sorting once again the same withusort.Then the final sort order is (Demo):
As there was a problem with stable sort and I’m also not that well with it (the suggested function looks a bit like an overhead to me in your case here), there is just good old
foreach.And as there are no duplicate values for those to be sorted later on, well, this leaves some nifty room:
It is just straight ahead for-eaching over the array, putting all values into
$resultalready that are not part of the ordering.Those that are part of the ordering are put into
$laterwith their order value as index already.Only
$laterthen is sorted withksort, and then the two parts are merged:And done. No callback function needed. Just first filtering, and the indexing with the sort value together with
ksortdoes the magic. Demo.