-1

I am trying to figure out how to sort an array by filesize strings correctly. I am building an array like the one shown below and would like to sort it by the filesize column. I have looked around but havent been able to find anything that has been too helpful in sorting it.

Array (
    [0] => Array (
        [0] => FileName 
        [1] => 71.6 MB 
        [2] => /path/to/file/
        [3] => 2018-12-08 19:31:17 ) 
    [1] => Array ( 
        [0] => FileName
        [1] => 1.15 GB 
        [2] => /path/to/file/
        [3] => 2018-12-08 17:36:28 ) 
    [2] => Array ( 
        [0] => FileName
        [1] => 10.22 MB 
        [2] => /path/to/file/
        [3] => 2018-12-08 16:13:24 ) 
    [3] => Array ( 
        [0] => FileName
        [1] => 34.42 MB 
        [2] => /path/to/file/
        [3] => 2018-12-08 16:24:27 ) 
    [4] => Array ( 
        [0] => FileName
        [1] => 466.18 KB 
        [2] => /path/to/file/
        [3] => 2018-12-08 16:31:44 ) 
    [5] => Array ( 
        [0] => FileName
        [1] => 26.98 MB 
        [2] => /path/to/file/
        [3] => 2018-12-08 17:34:57 )
    )

I am loading data from a database and I get and add the file sizes while building the array using this function.

function format_size($size) {
    $sizes = array(" Bytes", " KB", " MB", " GB", " TB", " PB", " EB", " ZB", " YB");
    if ($size == 0) { return('n/a'); } else {
    return (round($size/pow(1024, ($i = floor(log($size, 1024)))), 2) . $sizes[$i]); }
}

This is basically how I am building the array.

if ($stmt->rowCount() > 0) {
    $stmt->setFetchMode(PDO::FETCH_ASSOC);
    $iterator = new IteratorIterator($stmt);
    $data_array = Array();
    foreach ($iterator as $key => $row) {
        $fname = $row["filename"];
        $size = filesize('/path/to/file/' . $row["filename"]);
        $fsize = format_size($size);
        $fpath = '/path/to/file/' . $row["filename"];
        $f_tstamp = $row['timestamp'];

        $file_array = Array(
            $fname,
            $fsize,
            $fpath,
            $f_tstamp
            );

        $time[$key] = $row['timestamp'];
        $data_array[] = $file_array;
   }

This is how I sort by timestamps.

array_multisort($time, SORT_DESC, $data_array);
foreach ($data_array as $val) {
    print $val[0];
    print $val[1];
    print $val[2];
    print $val[3];
}

What can I do to sort it properly using the filesize strings? Thanks.

Richard
  • 445
  • 1
  • 5
  • 21
  • 3
    Would it be easier to just store the actual file size as bytes in another array element. – Nigel Ren Dec 09 '18 at 20:01
  • Maybe, i havent tried that. – Richard Dec 09 '18 at 20:03
  • I meant putting `$size` as say element `[4]` – Nigel Ren Dec 09 '18 at 20:05
  • Yeah you are probably right but I have it working now from the answer below. Thanks for your input. – Richard Dec 09 '18 at 20:09
  • 1
    The other problem you will have using the current method is that as you round to 2dp, a file may be 45 MB larger than another, but in GB it will be the same. – Nigel Ren Dec 09 '18 at 20:20
  • Yeah I just noticed that. Mostly everything is in MB and the data is just for me to glance at so it's not a huge problem but it would be nice to have it work properly for everything so I will check out the edited answer below again. – Richard Dec 09 '18 at 20:25

2 Answers2

1

In order to make sorting easier, you should store the numeric value returned by filesize inside the array, e.g. by adding it to the end like so:

$file_array = Array(
    $fname,
    $fsize,
    $fpath,
    $f_tstamp,
    $size
);

Then, it should be as simple as

usort($data_array, function($a, $b){ return $a[4] - $b[4]; });

The second parameter of usort is what's known as a comparator function in most languages. It receives two items of your array at a time, and expects an integer return value:

  • 0 means keep the elements in the order they are currently in
  • a negative value means $a is less than $b, so it should come before it
  • a positive value means $a is greater than $b, so it should come after it

The fourth index of each argument is your size value, and by subtracting the first from the second you will get an ascending sort order. You can reverse the operands ($b[4] - $a[4]) to get a descending sort order.

If you use PHP 7+ you can also use the spaceship operator here instead.

SeinopSys
  • 8,787
  • 10
  • 62
  • 110
  • 2
    So if I understand this correctly - your saying subtract `71.6 MB` from `1.15 GB` to sort this by file size? – Nigel Ren Dec 09 '18 at 20:14
  • @NigelRen OP contains a parser function which they use to calculate the corresponding numeric value in bytes: `$fsize = format_size($size);` – SeinopSys Dec 09 '18 at 20:15
  • Actually you are right, it's not parsing, it's actually converting to a string. Let me update my answer. – SeinopSys Dec 09 '18 at 20:17
  • @Richard Please see the updated answer, I misunderstood your `format_size` function the first time around, and the previous version will have issues. – SeinopSys Dec 09 '18 at 20:21
  • Thanks for the update. I noticed the issue as well but mostly everything is in MB and its just data for me to glance at so it wouldn't have been a huge issue but thanks for the update. I should have been converting the sizes after the sort to begin with but I thought maybe there was an easy way with how I was doing it so I figured it wouldn't hurt to ask. Thanks again. Thanks for your input as well @NigelRen – Richard Dec 09 '18 at 20:31
  • 1
    Props to @NigelRen for pointing the issue out, I initially thought `format_size` was being used to convert from the string seen in the first block of output to a number. I didn't read the question thoroughly enough. – SeinopSys Dec 09 '18 at 20:33
-1

You need to use php usort, Take a look at php.net usort

It takes a comparator function as second argument

Community
  • 1
  • 1
madz
  • 1,803
  • 18
  • 45