I am in a tricky situation where i need to sort an array by values that lie within its subarray, but i need the result to be staged. In other words, sorting should be done by one OR more priorities.

The problem is that the entire sorting process is configurable by the user, so hardcoding anything is not an option. I need to stay flexible, but i limit the options by providing predefined sort functions.

Let's get into it:

In this example we will sort a list of print formats. We will use only two possible properties.

The user configures the sorting process in an INI file:

sort_priority="special_deal:desc,ratio:asc"

Description:

// special_deal -> This is a binary flag - if set to 1 the print format is a special deal and should therefore be presented first
// ratio -> This is the ratio of the given print format (i.e. 0.75 (that's a 3:4 format) or 1 (that's a 1:1 format))

In the code, the configuration is broken apart:

$toSort=array(<OUR ARRAY WE WANT TO SORT>);

$sortKeys=explode(',', 'special_deal:desc,ratio:asc');

// we then iterate through the defined keys
foreach($sortKeys as $sortKey){

     // we put together the name of the predefined sort function
     if(strstr($sortKey, ':')) {
         list($skey,$sdir)=explode(':', $sortKey);
         $methodName='sort_by_'.$skey.'_'.$sdir;
     } else $methodName='sort_by_'.$sortKey.'_asc';

     // so $methodName can (for instance) be: sort_by_special_deal_asc
     // or: sort_by_ratio_desc

     // if the sort function is available, we apply it
     if(is_callable($methodName))
         usort($toSort, $methodName);
}

And our sort functions look like this:

function sort_by_special_deal_asc($a, $b){
    return ($a['specialDeal']!=$b['specialDeal']);
}
function sort_by_special_deal_desc($a, $b){
    return ($a['specialDeal']==$b['specialDeal']);
}
function sort_by_ratio_asc($a, $b){
    if($a==$b) return 0;
    return $a['ratio']<$b['ratio'] ? -1 : 1;
}
function sort_by_ratio_desc($a, $b){
    if($a==$b) return 0;
    return $a['ratio']>$b['ratio'] ? -1 : 1;
}

On the the problem at hand ...

The above solution works fine, but only for the last applied sort function. So when we iterate through the sort functions that are to be applied, each call to usort() will cause a reordering of all the elements in the array. The problem is, we want the sorting to be staged (or stacked), so in this given example that would mean, practically:

1.) Sort all entries so that the ones that are a special deal come first
2.) Then sort all entries by their ratio

Here is an example on how the data can look like:

$formats=array(
     array(
         'format' => '30x40',
         'ratio' => 0.75
     ),
     array(
         'format' => '60x90',
         'ratio' => 0.667
     ),
     array(
         'format' => '50x50',
         'ratio' => 1
     ),
     array(
         'format' => '60x80',
         'ratio' => 0.75,
         'specialDeal' => 1
     )
);

And the desired result, given the above sorting feature, should be:

$formats=array(
     array(
         'format' => '60x80',
         'ratio' => 0.75,
         'specialDeal' => 1
     ),
     array(
         'format' => '60x90',
         'ratio' => 0.667
     ),
     array(
         'format' => '30x40',
         'ratio' => 0.75
     ),
     array(
         'format' => '50x50',
         'ratio' => 1
     ),
);

I hope this explains the issue properly.

Can anybody point me in the right direction here? How can i achieve this, dynamically, at best by using usort() ?

Thank You!

EDIT: Please note - my comparison functions (see above) were faulty. There were two problems:

1.) Returning boolean was wrong - returning -1, 0 or 1 was the way to go. 2.) Comparison of $a and $b as complete arrays/objects was not correct - the right is to compare the specific values within those arrays (the ones the function is supposed to compare).

See the accepted answer and the according comments section for details.

有帮助吗?

解决方案

Build an array like this by parsing the user's sort preferences:

$sortMethods = array('sort_by_ratio_desc', 'sort_by_special_deal_asc');

Then sort using a comparison like this:

usort($array, function ($a, $b) use ($sortMethods) {
    foreach ($sortMethods as $method) {
        $result = $method($a, $b);
        if ($result != 0) {
            break;
        }
    }
    return $result;
});

其他提示

Check out the comments for uasort in the php.net manual - http://php.net/manual/en/function.uasort.php

Specifically the one with dynamic callbacks posted by dholmes.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top