Marcello Impastato
Marcello Impastato

Reputation: 2281

Sort multidimensional array by first element in specific 3rd level subarray -- sort by its key first, then by its value

I need to sort the rows of my multidimensional array by the first element of its subarray. Each row may have a dynamically named first element in its attribute subarray. I want to sort first by the first element's key, then by the value of the first element.

My input array looks like this:

$array = [
    [
        'tag' => 'meta',
        'type' => 'complete',
        'attributes' => ['property' => 'og:type', 'content' => 'website']
    ],
    [
        'tag' => 'meta',
        'type' => 'complete',
        'attributes' => ['name' => 'robots', 'content' => 'noindex, nofollow']
    ],
    [
        'tag' => 'meta',
        'type' => 'complete',
        'attributes' => ['name' => 'application', 'content' => 'My Application']
    ],
    [
        'tag' => 'meta',
        'type' => 'complete',
        'attributes' => ['http-equiv' => 'content-type', 'content' => 'text/html; charset=utf-8']
    ]
];

How I can sort it with array_multisort()?

Desired output:

Array
(
    [0] => Array
        (
            [tag] => meta
            [type] => complete
            [attributes] => Array
                (
                    [http-equiv] => content-type
                    [content] => text/html; charset=utf-8
                )
        )
    [1] => Array
        (
            [tag] => meta
            [type] => complete
            [attributes] => Array
                (
                    [name] => application
                    [content] => My Application
                )
        )
    [2] => Array
        (
            [tag] => meta
            [type] => complete
            [attributes] => Array
                (
                    [name] => robots
                    [content] => noindex, nofollow
                )
        )
    [3] => Array
        (
            [tag] => meta
            [type] => complete
            [attributes] => Array
                (
                    [property] => og:type
                    [content] => website
                )
        )
)

I am having some difficulty because the first column of attributes is unpredictably keyed.

Upvotes: 0

Views: 126

Answers (2)

mickmackusa
mickmackusa

Reputation: 47894

It will be most direct/performant to build two flat arrays from the attribute keys and values then use array_multisort() -- this involves no iterated function calls.

  • The [..] syntax in the first foreach() is a technique called "array destructuring" and allows you to isolate only the data that you need within the body of the loop.
  • The [] syntax in the nested foreach() signature pushes keys and values into the output arrays.
  • The break condition ensures that we never push more than the first element from each attribute subarray into the result array.

Code: (Demo)

foreach ($array as ['attributes' => $attr]) {
    foreach ($attr as $keys[] => $values[]) {
        break;
    }
}
array_multisort($keys, $values, $array);
var_export($array);

Upvotes: 0

u_mulder
u_mulder

Reputation: 54831

usort with custom callback will look like:

usort($arr, function($a, $b) {
   $aKeyFirst = array_key_first($a['attributes']);
   // fallback, php version < 7.3
   //$aKeyFirst = array_keys($a['attributes'])[0];
   
   $bKeyFirst = array_key_first($b['attributes']);
   // fallback, php version < 7.3
   //$bKeyFirst = array_keys($b['attributes'])[0];
   
   if ($aKeyFirst !== $bKeyFirst) {
       return strcmp($aKeyFirst, $bKeyFirst);
   } else {
       return strcmp($a['attributes'][$aKeyFirst], $b['attributes'][$bKeyFirst]);
   }
});

Upvotes: 1

Related Questions