Das Crova
Das Crova

Reputation: 11

PHP: sorting a multidimensional array by number of occurrences of the sub arrays

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!

Upvotes: 0

Views: 66

Answers (1)

Chris Haas
Chris Haas

Reputation: 55427

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]];
    }
);

Upvotes: 1

Related Questions