Why isn't my usort() output sorted?

2019-07-21 07:54发布

问题:

I'm rewriting an old script that spits out the most popular content using usort.

For some reason, the output of my usort isn't actually sorted.

I'm using php 5.5 (please disregard the use of the depreciated mysql_ function, that is part of the reason I am rewriting this script).

    //store data in array
    $sort_array = array();
    while($row = mysql_fetch_assoc($result)) {
        //calculate age
        $age = (strtotime("now") - strtotime($row["DATE"]))/86400;//86400 converts seconds to days
        //calculate "effective views" via gaussian distribution shown below 
        //y = e^(-(k * x)^2) where k is below
        $K = 0.1665109222315395512706329289790402095261177704528881;//solved for a half-life of 5 days
        $effective_views = round($row["VIEWS"] * exp(-pow( $K * $age, 2)));

        //store data
        $article = new stdClass;
        //$article->id = $row["ID"];
        $article->effective_views = $effective_views;
        //$article->title = $row["TITLE"];
        //$article->author = $row["AUTHOR"];
        $sort_array[] = $article;
    }

    //sort array based on effective views
    usort(
        $sort_array, 
        function($a, $b) {
            return -strcmp($a->effective_views, $b->effective_views);
        }
    );
    echo "<pre>";
    print_r($sort_array);

The output should be sorted on effective_views in descending order, however, it is not.

Here is an output dump:

http://pastebin.com/rV5YwWN7

Please inform me what I am doing wrong here.

回答1:

The problem is using strcmp to compare numbers; it compares strings, in a lexicographical manner, or "as found in a dictionary". This makes "96" come after "503", or before when ordered in reverse.

Consider the following which returns a negative number when a < b, a positive number when a > b, and 0 otherwise - it effectively reverse orders numbers when used with a usort-style compare function.

return $a->effective_views - $b->effective_views;


回答2:

You should not use strcmp to compare integers.

You should use:

return $a->effective_views - $b->effective_views;

instead of

return -strcmp($a->effective_views, $b->effective_views);

You can look also for result of:

echo strcmp(2,10);

As you see it's 1 and not -1 because first character in 10 string is 1 and 1 is before 2



回答3:

You are using strcmp on what appear to be integers, try return $a->effective_views - $b->effective_views or the other way around depending on your ordering.

This simple sandbox shows that strcmp works in lexicographical fashion.



回答4:

Strcmp - Binary safe string comparison

You can not use strcmp like that because it is not to compare numbers, if you want to compare numbers using strcmp you can use a little trick like this:

function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a > $b) ? -1 : 1;
}

$a = array(3, 7, 733, 9, 73, 222, 5, 99, 1, 5, 0);
usort($a, "cmp");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}

And if you want to use Comparison Operators:

function cmp2($a, $b) {
    if(strlen($a)!==strlen($b)){
    return -strcmp(strlen($a),strlen($b));
    }
    return -strcmp($a, $b);
}

$a = array(3, 7, 733, 9, 73, 222, 5, 99, 1, 5, 0);
usort($a, "cmp2");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}

Example: http://sandbox.onlinephpfunctions.com/



标签: php usort