phparrayssortingmultidimensional-arraycustom-sort

Sort a 2d array by the order of corresponding values in another 2d array


I have two multi-dimensional arrays and need to sort the first array in the same order as the second array based on different keys (but their values are the same). In the below example I need $allOptions to be sorted into the same order as $regOptions but based on the values of clID == optID.

However, not all $allOptions sub-arrays (clID) are present in $regOptions sub-arrays (optID)....so any non-matched elements in $allOptions would be thrown to the bottom/end of the array.

How can I do this?

$allOptions = array(
     array("clID"=> 171, ...other values),
     array("clID"=> 191, ...other values),
     array("clID"=> 131, ...other values),
     array("clID"=> 101, ...other values),
     array("clID"=> 201, ...other values),
     array("clID"=> 181, ...other values),
     ...
     array("clID"=> 99, ...other values),  // not in regOptions
     array("clID"=> 129, ...other values)  // not in regOptions
     array("clID"=> 139, ...other values)
    
) ;

$regOptions = array(
    array("order"=>1,"optID"=> 131, ...other values),
    array("order"=>2,"optID"=> 191, ...other values),
    array("order"=>3,"optID"=> 181, ...other values),
    array("order"=>4,"optID"=> 139, ...other values),
    array("order"=>5,"optID"=> 101, ...other values),
    array("order"=>6,"optID"=> 201, ...other values),
    array("order"=>7,"optID"=> 171, ...other values) 
    ...
) ;

So the output would be:

$allOptions = array(
     array("clID"=> 131, ...other values),
     array("clID"=> 191, ...other values),
     array("clID"=> 181, ...other values),
     array("clID"=> 139, ...other values)
     array("clID"=> 101, ...other values),
     array("clID"=> 201, ...other values),
     array("clID"=> 171, ...other values),
     ...
     array("clID"=> 99, ...other values),  // not in regOptions
     array("clID"=> 129, ...other values)  // not in regOptions
) ;

Solution

  • All earlier posted answers are working Waaaaaaaay too hard. Use array_column() to generate a lookup array. Use usort() or array_multisort() to sort by that priority array.

    Code: (Demo)

    $priority = array_column($regOptions, 'order', 'optID');
    usort(
        $allOptions,
        fn($a, $b) => ($priority[$a['clID']] ?? PHP_INT_MAX) <=> ($priority[$b['clID']] ?? PHP_INT_MAX)
    );
    var_export($allOptions);
    

    If you want to fallback to sorting by clID ASC for all of the unmentioned clId values, then use the elvis operator to break those ties with a simple 3-way comparison on the column value.

    $priority = array_column($regOptions, 'order', 'optID');
    usort(
        $allOptions,
        fn($a, $b) =>
            ($priority[$a['clID']] ?? PHP_INT_MAX) <=> ($priority[$b['clID']] ?? PHP_INT_MAX)
            ?: $a['clID'] <=> $b['clID']
    );
    

    The first script can be performed with array_multisort() in the following way. Note that this approach may perform better than usort() because it makes fewer lookups, but it does sort on $priority, then sort on $allOptions row length, then clId (how it would work if you called sort($allOptions); I imagine that the rows all have the same length though). (Demo)

    $priority = array_column($regOptions, 'order', 'optID');
    array_multisort(
        array_map(fn($v) => $priority[$v['clID']] ?? PHP_INT_MAX, $allOptions),
        $allOptions
    );
    var_export($allOptions);