1

I am working on a photo gallery that automatically sorts the photos based on the numbers of the file name.

I have the following code:

//calculate and sort 
$totaal = 0;
if($handle_thumbs = opendir('thumbs')){
    $files_thumbs = array();
    while(false !== ($file = readdir($handle_thumbs))){
        if($file != "." && $file != ".."){
            $files_thumbs[] = $file;
            $totaal++;
        }
    }
    closedir($handle_thumbs);
}
sort($files_thumbs);


//reset array list
$first = reset($files_thumbs);
$last = end($files_thumbs);
//match and split filenames from array values - image numbers
preg_match("/(\d+(?:-\d+)*)/", "$first", $matches);
$firstimage = $matches[1];
preg_match("/(\d+(?:-\d+)*)/", "$last", $matches);
$lastimage = $matches[1];

But when i have file names like photo-Aname_0333.jpg, photo-Bname_0222.jpg, it does start with the photo-Aname_0333 instead of the 0222.

How can i sort this by the filename numbers?

4 Answers 4

2

None of the earlier answers are using the most appropriate/modern technique to perform the 3-way comparison -- the spaceship operator (<=>).

Not only does it provide a tidier syntax, it also allows you to implement multiple sorting rules in a single step.

The following snippet break each filename string in half (on the underscore), then compare the 2nd half of both filenames first, and if there is a tie on the 2nd halves then it will compare the 1st half of the two filenames.

Code: (Demo)

$photos = [
    'photo-Bname_0333.jpg', 
    'photo-Bname_0222.jpg',
    'photo-Aname_0333.jpg', 
    'photo-Cname_0111.jpg', 
    'photo-Cname_0222.jpg',
    'photo-Aname_0112.jpg', 
];
usort($photos, function ($a, $b) {
   return array_reverse(explode('_', $a, 2)) <=> array_reverse(explode('_', $b, 2));
});
var_export($photos);

Output:

array (
  0 => 'photo-Cname_0111.jpg',
  1 => 'photo-Aname_0112.jpg',
  2 => 'photo-Bname_0222.jpg',
  3 => 'photo-Cname_0222.jpg',
  4 => 'photo-Aname_0333.jpg',
  5 => 'photo-Bname_0333.jpg',
)

For anyone who still thinks the preg_ calls are better, I will explain that my snippet is making potentially two comparisons and the preg_ solutions are only making one.

If you wish to only use one sorting criteria, then this non-regex technique will outperform regex:

usort($photos, function ($a, $b) {
   return strstr($a, '_') <=> strstr($b, '_');
});

I super-love regex, but I know only to use it when non-regex techniques fail to provide a valuable advantage.


Older and wiser me says, simply remove the leading portion of the string before sorting (use SORT_NATURAL if needed), then sort the whole array. If you are scared of regex, then make mapped calls of strtok() on the underscore.

Code: (Demo)

array_multisort(
    preg_replace('/.*_/', '', $photos),
    $photos
);

Or: Demo

array_multisort(
    substr_replace($photos, '', 0, -8),
    $photos
);
Sign up to request clarification or add additional context in comments.

Comments

1

usort is a php function to sort array using values. usort needs a callback function that receives 2 values.

In the callback, depending of your needs, you will be return the result of the comparision 1, 0 or -1. For example to sort the array asc, I return -1 when the firts value of the callback is less than second value.

In this particular case I obtain the numbers of the filename, and compare it as string, is not necesary to cast as integer.

<?php

$photos=[
    'photo-Bname_0222.jpg',
    'photo-Aname_0333.jpg', 
    'photo-Cname_0111.jpg', 

];

usort($photos, function ($a, $b) {

    preg_match("/(\d+(?:-\d+)*)/", $a, $matches);
    $firstimage = $matches[1];
    preg_match("/(\d+(?:-\d+)*)/", $b, $matches);
    $lastimage = $matches[1];

    if ($firstimage == $lastimage) {
        return 0;
    }
    return ($firstimage < $lastimage) ? -1 : 1;
});

print_r($photos);

2 Comments

This one is working, but can you explain a bit of it please? :)
I add an explanation Rick
0

It sorts alphabetically because you use sort() on the filename. The 2nd part of your code does nothing.

You might want to take a look at usort http://php.net/manual/en/function.usort.php You can do something like

function cmp($a, $b) {
    if ($a == $b) {
        return 0;
    }
    preg_match('/(\d+)\.\w+$/', $a, $matches);
    $nrA = $matches[1];
    preg_match('/(\d+)\.\w+$/', $b, $matches);
    $nrB = $matches[1];

    return ($nrA < $nrB) ? -1 : 1;
}

usort($files_thumb, 'cmp');

Also, I'm not sure about your regex, consider a file named "abc1234cde2345xx". The one I used takes the last digits before a file extension at the end. But it all depends on your filenames.

2 Comments

But how to do this with 100+ files in a folder. I forgot, after the preg_match i have: //set imagenr if(!isset($_GET["image"])){$imagenr = $firstimage;} else{$imagenr = $_GET["image"];} $firstimage should match the lowest number in the folder and $lastimage the latest. so i thought i could use the preg_match to split the numbers from the filename and start counting from the first number till latest availible
$files_thumb is an array containing all 100+ filenames, you collected them in the while loop. usort takes this array and applies a custom sorting function. so after that $files_thumb will still be the array containing all the files, just sorted by the file number.
0
sort(array,sortingtype) , you have to set the second parameter of the sort() function to 1 so it will sort items numerically 



 //calculate and sort 
$totaal = 0;
if($handle_thumbs = opendir('thumbs')){
    $files_thumbs = array();
    while(false !== ($file = readdir($handle_thumbs))){
        if($file != "." && $file != ".."){
            $files_thumbs[] = $file;
            $totaal++;
        }
    }
    closedir($handle_thumbs);
}
sort($files_thumbs,1);
//reset array list
$first = reset($files_thumbs);
$last = end($files_thumbs);
//match and split filenames from array values - image numbers
preg_match("/(\d+(?:-\d+)*)/", "$first", $matches);
$firstimage = $matches[1];
preg_match("/(\d+(?:-\d+)*)/", "$last", $matches);
$lastimage = $matches[1];

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.