I have an array that contains other associative arrays as its values. Each one of these "sub" arrays is mapped with key-value pairs:
$items = [
['type' => 1, 'text' => 'A'],
['type' => 2, 'text' => 'B'],
['type' => 2, 'text' => 'C'],
['type' => 3, 'text' => 'D'],
['type' => 2, 'text' => 'E'],
['type' => 1, 'text' => 'F'],
['type' => 4, 'text' => 'G'],
['type' => 2, 'text' => 'H'],
['type' => 1, 'text' => 'I'],
['type' => 4, 'text' => 'J'],
['type' => 2, 'text' => 'K'],
['type' => 4, 'text' => 'L'],
['type' => 2, 'text' => 'M'],
['type' => 3, 'text' => 'N'],
['type' => 3, 'text' => 'O'],
['type' => 1, 'text' => 'P'],
['type' => 1, 'text' => 'Q'],
['type' => 2, 'text' => 'R'],
['type' => 3, 'text' => 'S'],
['type' => 2, 'text' => 'T'],
['type' => 3, 'text' => 'U'],
['type' => 2, 'text' => 'V'],
['type' => 3, 'text' => 'W'],
['type' => 2, 'text' => 'X'],
['type' => 4, 'text' => 'Y'],
['type' => 2, 'text' => 'Z'],
];
How can I sort this array and return as another array, with the following requirements?
- It needs to sort the associative arrays by its
type
. - It needs to do this by picking the first 3 items that show up in the
$items
array, and then moving on to the next type, which is2
, and doing the same. The max amount oftypes
there are in my code is8
, but theoretically it should work with any number. - If it has picked out 3 of each type one time, restart again with the remainder of items in the array, starting at type
1
again.
Essentially you should get another array which -- from top to bottom -- has values with their types in alternated orders, something like this:
[
['type' => 1, 'text' => 'A'],
['type' => 1, 'text' => 'F'],
['type' => 1, 'text' => 'I'],
['type' => 2, 'text' => 'B'],
['type' => 2, 'text' => 'C'],
['type' => 2, 'text' => 'E'],
['type' => 3, 'text' => 'D'],
['type' => 3, 'text' => 'N'],
['type' => 3, 'text' => 'O'],
['type' => 4, 'text' => 'G'],
['type' => 4, 'text' => 'J'],
['type' => 4, 'text' => 'L'],
['type' => 1, 'text' => 'P'],
['type' => 1, 'text' => 'Q'],
['type' => 2, 'text' => 'H'],
['type' => 2, 'text' => 'K'],
['type' => 2, 'text' => 'M'],
['type' => 3, 'text' => 'S'],
['type' => 3, 'text' => 'U'],
['type' => 3, 'text' => 'W'],
['type' => 4, 'text' => 'Y'],
['type' => 2, 'text' => 'R'],
['type' => 2, 'text' => 'T'],
['type' => 2, 'text' => 'V'],
['type' => 2, 'text' => 'X'],
['type' => 2, 'text' => 'Z'],
]
The text
does not matter to the sorting.
I've actually gone as far as to make a YouTube video to better explain what should happen: Video
My current thought process was to use a separate array called $groups
, use a foreach
loop eight different times, and in each one, continue the loop if it's not for the specific type. For example:
// My above $items array example here
// Initialize $groups
$groups = [];
// foreach for type 1
foreach ($items as $item) {
// If any of the items are not of type 1, ignore and continue
if ($item["type"] !== 1) {
continue;
}
// Otherwise, push to $groups
array_push($groups, $item["type"];
}
// foreach for type 2
foreach ($items as $item) {
// If any of the items are not of type 2, ignore and continue
if ($item["type"] !== 2) {
continue;
}
// Otherwise, push to $groups
array_push($groups, $item["type"];
}
// Do this for each possible type, up to type 8
This not only does this seem extremely inefficient and backwards, I just feel there is a better way to go about it or another angle I'm missing. I don't believe the above code is even close to the solution.
I think setting up
array_multisort()
to do this job makes logical sense (since this is a sorting task).Note that my demonstration will sort the actual input array (make yourself a copy if you need to preserve the original).
array_multisort()
to sort on$grouper
, then$column
, then$items
(then you can access$items
).Code: (Demo) (Alt Demo)
Somewhat related is Sort a flat array in recurring ascending sequences which has a hardcoded grouping N of 1 (instead of 3) while sorting data in ascending recurring sequences.
Alternatively, you can group the data into subsets, then sort, then consume the grouped data while populating the new result.
Though, I will state that this doesn't feel as professional as the
array_multisort()
approach for a sorting task.type
value.foreach()
loop (notice the&
in the signature) to push only the first N rows per group into the result array. If the current group still has elements, push the whole group onto the end of the array.Code: (Demo)
As a microoptimization, you can extract the entire payload of the last remaining group with: (Demo)