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

229 Views Asked by At
 $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]);
        });
3

There are 3 best solutions below

0
Will B. On BEST ANSWER

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');
1
Faiz Sandhi On

Please have a look at bellow snippet

echo '<pre>';
function ShortFunction($new,$old){
    return floatval($old[5]) <=> floatval($new[5]);
}
$new_array = usort($unresponsives,'ShortFunction');
print_r($unresponsives);
2
mickmackusa On

I recommend two steps.

  1. Populate an array with ready-to-compare numeric values.
  2. Call array_multisort() to compare in a DESC direction.

Code: (Demo)

array_multisort(
    array_map(
        fn($a) => sscanf($a[5], '%f')[0] ?? PHP_INT_MIN,
        $unresponsives
    ),
    SORT_DESC,
    $unresponsives
);
var_export($unresponsives);

This can also be performed with absolutely no iterated function calls for better efficiency.

The first rule checks for Undefined values and orders true evaluations before false evaluations. The second rule is only applied when a tiebreaker is required -- in which case the float values are compared for a DESC sort direction.

Code: (Demo)

usort(
    $unresponsives,
    fn($a, $b) => 
        [$b[5] === 'Undefined', (float) $b[5]]
        <=>
        [$a[5] === 'Undefined', (float) $a[5]]
);
var_export($unresponsives);

A similar approach can be found here.