0

I want to sort a multidimensional array by appearances of the number of subarrays.

This is my multidimensional array (it could hold any subarrays holding any values randomly ordered, this is just for testing purposes):

$items = array (
array ("00008", "Metal", "Melvins", "Working With God", "Sub Pop", "SP 009"), 
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00021", "Techno", "Laurent Garnier", "Water Planet", "F Communications", "SDB00015"), 
array ("00056", "LP", "Communards", "Communards", "RCA", "E 342-F"), 
array ("00056", "LP", "Communards", "Communards", "RCA", "E 342-F"));

So that after sorting the multidimensional array becomes:

$items = array (
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00019", "LP", "Ray Parker", "The Other Woman", "EMI", "EMI02"), 
array ("00056", "LP", "Communards", "Communards", "RCA", "E 342-F"), 
array ("00056", "LP", "Communards", "Communards", "RCA", "E 342-F"), 
array ("00008", "Metal", "Melvins", "Working With God", "Sub Pop", "SP 009"), 
array ("00021", "Techno", "Laurent Garnier", "Water Planet", "F Communications", "SDB00015"));

The purpose is that the subarray occuring three times appears first, followed by the subarray appearing two times. All the subarrays occurring one time can appear in random order.

Can it be done using a one-liner?

For example: sorting out the uniques can be done like this:

$uniques = array_intersect_key ($items, array_unique (array_map ("serialize", $items)));

Many thanks for helping me out!

1
  • 1
    When you say "appearances of the number of subarrays", you mean by the first item in the sub array which is like an ID, right? So 00019 is first because there are three sub arrays with that ID-like thing. Commented Dec 6, 2021 at 23:36

1 Answer 1

1

This seems to be what you are looking for. As for a one-liner, not sure.

// Grab the first column of each sub array, and then create an array
// mapping that ID to the number of times it was found in the main array
$counts = array_count_values(array_column($items, 0));

// Sort in reverse by value so that the greater quantity is first
arsort($counts);

// Custom sort where we look up the first column in our `$count` array
// and perform a spaceship comparison, going $b to $a so that the
// greater counts come first again.
usort(
    $items,
    static fn($a, $b) => $counts[$b[0]] <=> $counts[$a[0]]
);

Demo here: https://3v4l.org/Lm7L9

I should say, I'm not necessarily advocating for this code, it feels pretty cryptic, but it appears to work. The fact that there's more comment than code is a code-smell to me.

Edit
To convert this to PHP 7.0, you can just replace the arrow function in the usort with a traditional anonymous one:

usort(
    $items,
    static function ($a, $b) use ($counts) {
        return $counts[$b[0]] <=> $counts[$a[0]];
    }
);
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you very much for your answer. This indeed is exactly what i was looking for. Unfortunately, i am stuck to php 7.0.33, so i get the parse error. Nevertheless, this answer is of great educational value to me. Could never have found it myself.
@DasCrova, I've updated this for PHP 7.0
I recommend completely removing arsort() because it is a useless step. 3v4l.org/thILB It will serve just as well as an unsorted lookup array. @Chris
Few devs know that non-custom sorting algorithms sort by size before sorting by actual data. Another approach (which I'll not bother posting as an answer), is to group, then rsort, then flatten 3v4l.org/kvcqY -- but see how size-ties are broken by the first column value (21 comes before 8). Just a nice trick to keep up one's sleeve.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.