I have an array of rows from a database I need to sort by two parameters. I have tried to use the usort() function to accomplish this, but I’m running into some trouble.
Here is my code:
if ($sort == 'rating-desc') usort($records, array('browse_model', 'cmp'));
private function cmp($a, $b) {
$ratingCmp = strcmp($b['rating'], $a['rating']);
if ($ratingCmp == 0) {
return strcmp($b['title'], $a['title']);
} else {
return $ratingCmp;
}
}
Here is the print_r() result of the array before usort():
Array
(
[0] => Array
(
[isbn] => 1847199488
[title] => CodeIgniter 1.7
[rating] => 3.5
)
[1] => Array
(
[isbn] => 059615593X
[title] => CSS Cookbook, 3rd Edition (Animal Guide)
[rating] => 3.5
)
[2] => Array
(
[isbn] => 0321637984
[title] => Essential Facebook Development: Build Successful Applications for the Facebook Platform
[rating] => 3.5
)
[3] => Array
(
[isbn] => 0980576857
[title] => jQuery: Novice to Ninja
[rating] => 4.5
)
[4] => Array
(
[isbn] => 0596157134
[title] => Learning PHP, MySQL, and JavaScript: A Step-By-Step Guide to Creating Dynamic Websites (Animal Guide)
[rating] => 4.5
)
)
And here is the the result after the usort() (sorted by rating, but not by title):
Array
(
[0] => Array
(
[isbn] => 0980576857
[title] => jQuery: Novice to Ninja
[rating] => 4.5
)
[1] => Array
(
[isbn] => 0596157134
[title] => Learning PHP, MySQL, and JavaScript: A Step-By-Step Guide to Creating Dynamic Websites (Animal Guide)
[rating] => 4.5
)
[2] => Array
(
[isbn] => 0321637984
[title] => Essential Facebook Development: Build Successful Applications for the Facebook Platform
[rating] => 3.5
)
[3] => Array
(
[isbn] => 1847199488
[title] => CodeIgniter 1.7
[rating] => 3.5
)
[4] => Array
(
[isbn] => 059615593X
[title] => CSS Cookbook, 3rd Edition (Animal Guide)
[rating] => 3.5
)
)
So it puts them in order by rating, but not by title. How can I modify this so it sorts by rating, then by title?
For what it’s worth, I’ve also tried this:
if ($sort == 'rating-desc') {
foreach ($records as $key => $row) {
$rating[$key] = $row['rating'];
$title[$key] = $row['title'];
}
array_multisort($title, SORT_DESC, $rating, SORT_ASC, $records);
}
But that result isn’t correct either. It shows the same result as the usort I tried above.
What am I missing?
Thanks much,
Marcus
EDIT: Following some of the suggestions below, here are several things I’ve tried. None of them solve my problem. They all return the ratings sorted properly, but the title is descending.
private function cmp($a, $b) {
if ($a['rating'] == $b['rating']) {
return strcasecmp($b['title'], $a['title']);
}
return $b['rating'] - $a['rating'];
}
private function cmp($a, $b) {
if ($a['rating'] == $b['rating'])
return strcasecmp($a['title'], $b['title']);
return $b['rating'] - $a['rating'];
}
if ($sort == 'rating-desc') {
foreach ($records as $key => $row) {
$rating[$key] = $row['rating'];
$title[$key] = $row['title'];
}
array_multisort($rating, SORT_DESC, $title, SORT_ASC, $records);
}
if ($sort == 'rating-desc') {
foreach ($records as $key => $row) {
$rating[$key] = $row['rating'];
$title[$key] = $row['title'];
}
array_multisort($rating, SORT_DESC, $title, SORT_DESC, $records);
}
Here is my entire code example – the entire model…
class Browse_model extends Model {
function Browse_model() {
parent::Model();
}
function get_form_tags() {
$sql = 'select t.id, t.tag, coalesce(btc.count, 0) as count from tags as t left outer join (select tag_id, count(*) as count from books_tags group by tag_id) as btc on t.id = btc.tag_id order by count desc, tag asc';
$query = $this->db->query($sql);
$tags = $query->result();
return $tags;
}
function get_book_info($tags, $andor, $read, $sort) {
/*
* SELECT b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag
* FROM books AS b
* INNER JOIN books_tags AS bt ON b.isbn = bt.book_id
* INNER JOIN tags AS t ON bt.tag_id = t.id
* ORDER BY b.title, t.tag
*/
switch ($sort) {
case 'alpha-desc':
$order = 'b.title DESC, t.tag';
break;
case 'date-desc':
$order = 'b.date DESC, b.title, t.tag';
break;
case 'date-asc':
$order = 'b.date, b.title, t.tag';
break;
default:
$order = 'b.title, t.tag';
break;
}
$this->db->select('b.isbn, b.title, b.publisher, b.date, b.thumb, b.filename, b.pages, t.tag');
$this->db->from('books AS b');
$this->db->join('books_tags AS bt', 'b.isbn = bt.book_id', 'inner');
$this->db->join('tags AS t', 'bt.tag_id = t.id', 'inner');
$this->db->order_by($order);
$query = $this->db->get();
$result = $query->result();
$counter = '';
$records = $meta = $tags = array();
$count = count($result);
$i = 1;
foreach ($result as $book) {
// If this is not the last row
if ($i < $count) {
// If this is the first appearance of this book
if ($counter != $book->isbn) {
// If the meta array already exists
if ($meta) {
// Add the combined tag string to the meta array
$meta['tags'] = implode(', ', $tags);
// Add the meta array
$records[] = $meta;
// Empty the tags array
$tags = array();
}
// Reset the counter
$counter = $book->isbn;
// Grab the book from Amazon
$amazon = $this->amazon->get_amazon_item($book->isbn);
// Collect the book information
$meta = array(
'isbn' => $book->isbn,
'title' => strip_slashes($book->title),
'publisher' => strip_slashes($book->publisher),
'date' => date('F j, Y', strtotime($book->date)),
'thumb' => $book->thumb,
'file' => $book->filename,
'pages' => $book->pages,
'rating' => $amazon->Items->Item->CustomerReviews->AverageRating,
'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews
);
// Add the tag to the tags array
$tags[] = $book->tag;
} else {
// All we need is the tag
$tags[] = $book->tag;
}
// If this is the last row
} else {
// If this is the first appearance of this book
if ($counter != $book->isbn) {
// Grab the book from Amazon
$amazon = $this->amazon->get_amazon_item($book->isbn);
// Collect the book information
$meta = array(
'isbn' => $book->isbn,
'title' => strip_slashes($book->title),
'publisher' => strip_slashes($book->publisher),
'date' => date('F j, Y', strtotime($book->date)),
'thumb' => $book->thumb,
'file' => $book->filename,
'pages' => $book->pages,
'rating' => $amazon->Items->Item->CustomerReviews->AverageRating,
'raters' => $amazon->Items->Item->CustomerReviews->TotalReviews
);
}
// All we need is the tag
$tags[] = $book->tag;
// Add the combined tag string to the meta array
$meta['tags'] = implode(', ', $tags);
// Add the meta array
$records[] = $meta;
}
$i++;
}
echo '<code><pre>';
print_r($records);
echo '</pre></code>';
if ($sort == 'rating-desc') usort($records, array('browse_model', 'cmp'));
echo '<code><pre>';
print_r($records);
echo '</pre></code>';
return $records;
}
private function cmp($a, $b) {
if ($a['rating'] == $b['rating'])
return strcasecmp($b['title'], $a['title']);
return $b['rating'] - $a['rating'];
}
}
You could alternatively use array_multisort()
deleted code
Creates a couple extra arrays, but gets the job done; if you put the code into a function or a method as you’re doing (pass $records by reference) those will get destroyed on function exit.
Edit: I see you’ve put into your question that you’ve tried this. How doesn’t it work?
Edit: This works for me:
Another edit:
Given this array:
This code:
Will produce this: