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.
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;
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
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.
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/