Sorting a 12 digit array of strings in PHP numerically

87 Views Asked by At

I have some PHP code which is doing a sort for me on my array of numbers. The numbers in particular (for example) are as followed:

"040613002625", "040613000277", "040613000511", "040613001685", "040613003275", etc

When I sort my values however, 002625 is appearing before 001685 which is incorrect.

Note that I am using pagination on my page. I have 190 pages with listings of 30 items per page. The pagination could very well be the reason behind the issue as it's not sorting all 5,000 listings and instead it's only reading the first 30 and doing a sort on that single page. It's also incredibly slow to read and render the page (upwards of 1 minute).

Here is a snippet of my existing code showing the sort logic. Does it look correct?

$per_page = 30;
$current_page = $this->get_pagenum();
$a = $this->users_data;
$this->users_data = array_slice($a, (($current_page - 1) * $per_page), $per_page);

// call the sorting function
usort($this->users_data, array(&$this, 'usort_reorder'));

// sorting function
function usort_reorder($a, $b)
{
    // if no sort, default to user_login
    $orderby = (!empty($_GET['orderby'])) ? $_GET['orderby'] : 'ID';

    // if no order, default to asc
    $order = (!empty($_GET['order'])) ? $_GET['order'] : 'asc';

    // determine sort order
    $result = strcmp($a[$orderby], $b[$orderby]);
    
    // send final sort direction to usort
    return ($order === 'asc') ? $result : -$result;
}

Thanks.

1

There are 1 best solutions below

0
Jacob On

The issue your having with the sorting is likely because you are using strcmp which is used to sort strings, not numeric values. Read more about strcmp here: php strcmp function. Since your numbers are technically stored as strings right now, you'll need to convert them to integers or floats.

At a quick glance something like this should work:

function usort_reorder($a, $b)
{
    // if no sort, default to user_login
    $orderby = (!empty($_GET['orderby'])) ? $_GET['orderby'] : 'ID';

    // if no order, default to asc
    $order = (!empty($_GET['order'])) ? $_GET['order'] : 'asc';

    // convert string to integer for numeric comparison
    $val1 = intval($a[$orderby]);
    $val2 = intval($b[$orderby]);

    // determine sort order
    if ($val1 < $val2) {
        return ($order === 'asc') ? -1 : 1;
    } elseif ($val1 > $val2) {
        return ($order === 'asc') ? 1 : -1;
    } else {
        return 0;
    }
}

In this function:

  1. intval() is used to convert values to integers.
  2. The function returns -1, 1, or 0 based on whether the first value is less than, greater than, or equal to the second value

Although this should work, it would be more efficient to sort the data in database query instead. That would look like this:

$per_page = 30;
$current_page = $this->get_pagenum();
$offset = ($current_page - 1) * $per_page;

$query = "SELECT * FROM users ORDER BY CAST(user_id AS UNSIGNED) ASC LIMIT $per_page OFFSET $offset";

Edit: As per your comment

it comes from a database. It might be worth sorting there but as new entries are added, I assume the database will need to be continually sorted?

When you sort data in a query using ORDER BY, the database doesn't permanently sort the data in the table, it sorts the results each time you execute the query. This means that whenever new entries are added to the database, they are not automatically sorted within the database table. However, each time you run the query, the returned result will be sorted according to your criteria, including any new data that has been added since the last query.