phparrayssortingusort

PHP sorting array of non-associative arrays but having string values at the beginning


 $unresponsives =   [
    [
        "Customer",
        "172.52.46.75",
        "2022-04-01 16:20:45",
        "1817",
        "nxlog",
        "2327.02 Hours"
    ],
    [
        "Customer",
        "172.25.89.45",
        "2022-04-01 16:20:45",
        "1817",
        "nxlog",
        "2327.02 Hours"
    ],
    [
        "Customer",
        "172.19.10.94",
        "2022-04-01 16:20:45",
        "1817",
        "nxlog",
        "2327.02 Hours"
    ]]

This is an example from my array of arrays. I want to sort the arrays inside by their fifth element (hours) in descending order. I am able to achieve this with usort but in the array there are also arrays with the string "Undefined" as their fifth value. Example below:

[
        "PreProd",
        "178.18.15.12",
        "\/",
        "1502",
        "iis",
        "Undefined"
    ]

Currently they are listed at the bottom of the array after the sorting is done. I instead want them to be listed in the beginning of the array. So first arrays with undefined ones and then the rest in descending order. How can I achieve this?

Below is the usort function that I use:

usort($unresponsives, function ($unresponsive1, $unresponsive2) {
            return floatval($unresponsive2[5]) <=> floatval($unresponsive1[5]);
        });

Solution

  • array_multisort Approach - Example https://3v4l.org/SuJQX

    For a slightly more complex approach that allows fine-grained control of the sorting as desired. Use array_column to retrieve the hours. Use array_keys on the hours to determine the filtered value positions and iterate over the positions to assign them as a numeric value. Then array_multisort can be used with the desired flags to sort the resulting arrays.

    $hours = array_column($unresponsives, 5);
    if ($undefined = array_keys($hours, 'Undefined')) { 
        $hours = array_replace($hours, array_fill_keys($undefined, PHP_INT_MAX));
    }
    array_multisort($hours, SORT_DESC, SORT_NUMERIC, $unresponsives);
    

    Result

    var_export($unresponsives);
    
    array (
      0 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => 'Undefined',
      ),
      1 => 
      array (
        0 => 'Customer',
        1 => '172.25.89.45',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => 'Undefined',
      ),
      2 => 
      array (
        0 => 'Customer',
        1 => '172.52.46.75',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2328.02 Hours',
      ),
      3 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2324.02 Hours',
      ),
      4 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2322.02 Hours',
      ),
    )
    

    Ascending Numeric Sorting

    To change the sort order to SORT_ASC swap out PHP_INT_MAX for PHP_INT_MIN, depending on where in the array you want the filtered value to reside.

    $hours = array_column($unresponsives, 5);
    if ($undefined = array_keys($hours, 'Undefined')) {    
        $hours = array_replace($hours, array_fill_keys($undefined, PHP_INT_MIN));
    }
    array_multisort($hours, SORT_ASC, SORT_NUMERIC, $unresponsives);
    

    Result

    var_export($unresponsives);
    
    array (
      0 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => 'Undefined',
      ),
      1 => 
      array (
        0 => 'Customer',
        1 => '172.25.89.45',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => 'Undefined',
      ),
      2 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2322.02 Hours',
      ),
      3 => 
      array (
        0 => 'Customer',
        1 => '172.19.10.94',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2324.02 Hours',
      ),
      4 => 
      array (
        0 => 'Customer',
        1 => '172.52.46.75',
        2 => '2022-04-01 16:20:45',
        3 => '1817',
        4 => 'nxlog',
        5 => '2328.02 Hours',
      ),
    )
    

    usort Approach - Example https://3v4l.org/Uoml6

    The same methodology of assigning the filtered value as a numeric value can be applied to usort by manipulating the compared values of the desired filtered value(s) in a conditional, which will only be used to sort the array.

    Descending Order

    function compd($a, $b)
    {
        /*
        # Condensed Syntax
        return ('Undefined' === $b[5] ? PHP_INT_MAX : floatval($b[5])) <=> ('Undefined' === $a[5] ? PHP_INT_MAX : floatval($a[5]));
        */
    
         $v1 = $a[5];
         $v2 = $b[5];
         if ('Undefined' === $v1) {
             $v1 = PHP_INT_MAX;
         }
         if ('Undefined' === $v2) {
             $v2 = PHP_INT_MAX;
         }
    
         return floatval($v2) <=> floatval($v1);
    }
    
    usort($unresponsives, 'compd');
    

    Ascending Order

    As with the array_multisort approach change the sort order by swapping PHP_INT_MAX with PHP_INT_MIN but also swap the $b[5] <=> $a[5] comparison with $a[5] <=> $b[5] to sort the values in ascending order.

    function compa($a, $b) 
    {
        /*
        # Condensed Syntax
        return ('Undefined' === $a[5] ? PHP_INT_MIN : floatval($a[5])) <=> ('Undefined' === $b[5] ? PHP_INT_MIN : floatval($b[5]));
        */
    
         $v1 = $a[5];
         $v2 = $b[5];
         if ('Undefined' === $v1) {
             $v1 = PHP_INT_MIN;
         }
         if ('Undefined' === $v2) {
             $v2 = PHP_INT_MIN;
         }
    
         return floatval($v1) <=> floatval($v2);
    }
    
    usort($unresponsives, 'compa');