Я переписываю старый скрипт, который выплескивает самый популярный контент, используя usort.
По какой-то причине вывод моего usort на самом деле не сортируется.
Я использую php 5.5 (пожалуйста, не обращайте внимания на использование амортизированной функции mysql_, которая является частью причины, по которой я переписываю этот скрипт).
//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);
Результат должен быть отсортирован по эффективным_views в порядке убывания, однако это не так.
Вот выходной дамп:
http://pastebin.com/rV5YwWN7
Пожалуйста, сообщите мне, что я делаю неправильно здесь.
Проблема заключается в использовании strcmp
для сравнения чисел ; он сравнивает строки , в лексикографической манере, или «как найденные в словаре». Это означает, что «96» приходит после «503» или раньше, когда заказывается в обратном порядке.
Рассмотрим следующее, которое возвращает отрицательное число, когда a <b, положительное число при a> b и 0 в противном случае – оно эффективно отменяет порядковые номера при использовании с функцией сравнения стиля usort.
return $a->effective_views - $b->effective_views;
Вы не должны использовать strcmp
для сравнения целых чисел.
Вы должны использовать:
return $a->effective_views - $b->effective_views;
вместо
return -strcmp($a->effective_views, $b->effective_views);
Вы также можете посмотреть результат:
echo strcmp(2,10);
Как вы видите, это 1
а не -1
потому что первый символ из 10
строк равен 1
и 1
до 2
Вы используете strcmp
для того, что кажется целым, попробуйте return $a->effective_views - $b->effective_views
или наоборот, в зависимости от вашего заказа.
Эта простая песочница показывает, что strcmp
работает в лексикографическом стиле.
Strcmp – сравнение двоичных безопасных строк
Вы не можете использовать strcmp, потому что это не сравнение чисел, если вы хотите сравнивать числа с помощью strcmp, вы можете использовать небольшой трюк, подобный этому:
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"; }
И если вы хотите использовать Операторы сравнения :
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"; }