Sort array elements into a specific order

General discussions related to php

Moderators: egami, macek, gesf

Post Reply
guvna
php-forum Active User
php-forum Active User
Posts: 34
Joined: Fri Mar 10, 2017 7:42 am

Sat Sep 26, 2020 9:14 am

This topic is a continuation from:
viewtopic.php?f=13&t=29211

The code has been updated to now automatically create the gallery based on the images found in the folder the code is being run from. All this works fine but the order is whatever the order is they are found in the folder (alphabetically).

Code: Select all

<div class="property_text-container">
	<?php echo 'Gallery for ' . $pid_text; ?>
</div>

<!-- Slideshow container -->
<div class="slideshow-container">
<?php
	define('IMAGEPATH_15', dirname(__FILE__).'/'); // '/' is required.
	$img_height = 'height: 400px'; // default image height for gallery.
	$img_width = 'width: 600px'; // default image width for gallery.
	$img_counter_15 = 0;
	$img_style_15 = 'style="' .  $img_width . '"'; // only needs to be set once here.
	
	$img_total_15 = 0;
	// Calculate total number of images before displaying <div>
	// If calculated inside foreach() $img_counter always equaks $img_total.
	$myVar = count(glob(IMAGEPATH_15."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE)); 
	$img_total_15 = $myVar;
	
	/* //Alternate way of calculating number of images:
	foreach(glob(IMAGEPATH_15."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE) as $var){
		$img_total_15 ++; // Calculate total number of images before displaying <div>.
	} */
		
	foreach(glob(IMAGEPATH_15."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE) as $filename_15){
		// $img_total_15 ++; // Needs to be calculated outside foreach().
		$img_name_15 = pathinfo($filename_15, PATHINFO_FILENAME);
		$img_src_15 = basename($filename_15);
		
		echo '<div class="mySlides fade">';
			echo '<div class="numbertext">';
			$img_counter_15 ++;
			echo $img_counter_15 . ' / ' . $img_total_15;
			echo '</div>';
			
			//$img_src = "ext_front.jpg";
			// echo '<img src="' . $img_src . '" ' . $img_style . '>';
			echo '<img src="' . $img_src_15 . '" ' . $img_style_15 . '>'; // May need backlash before closing chevron.
			
			echo '<div class="text">';
				echo $pid_text . '<br>' . img_label($img_name=$img_src_15);
			echo '</div>';
		echo '</div>';
	}
?>

    <!-- Next and previous buttons -->
    <a class="prev" onclick="plusSlides(-1)">&#10094;</a>
    <a class="next" onclick="plusSlides(1)">&#10095;</a>  

</div><!-- END: div class="slideshow-container"-->
<br>
<!-- Dot container -->
<div class="dot-container">
    <!-- The dots/circles -->
    <div style="text-align:center">
        <?php
			$slide_counter = 0;
			while ($slide_counter < $img_total_15) {
				$slide_counter ++;
				echo '<span class="dot" onclick="currentSlide(' . $slide_counter . ')"></span>&nbsp;';
			}
		?>
    </div> 
</div>
Is there a way of setting a specific sort order depending on the opening sequence of characters of the image name?
I already have the elements in $filename.
Example:
ext_f: displays all front of property
ext_r: displays all rear of property
ext_p: displays pool/spa
rm_l: living room
rm_d: dining room
etc

I looked at uasort() and usort() but not sure if these are the functions I should be looking at.
simonbrahan
php-forum Fan User
php-forum Fan User
Posts: 90
Joined: Mon Jun 08, 2020 2:00 am
Contact:

Mon Oct 05, 2020 12:54 pm

usort is what you should be looking at. You can use pathinfo to get just the image file name, then you need to check the start of the filename for your key. Then you need to specify what order those keys are shown in.

Code: Select all

/**
 * Return the list of accepted property part keys, in the order they should be displayed
 *
 * @return string[]
 */
function getOrder()
{
    return [
        'ext_f',
        'ext_r',
        'ext_p',
        'rm_l',
        'rm_d'
    ];
}

/**
 * Given a file path (eg "slideshow/ext_f_decking.png")
 * Return the key describe the part of the property (eg "ext_f")
 * If there is no key, return false
 *
 * @param string
 *
 * @return mixed
 */
function getRoomFromFilePath($path)
{
    $filename = pathinfo($path, PATHINFO_FILENAME);

    foreach (getOrder() as $room_key) {
        if (strpos($filename, $room_key) === 0) {
            return $room_key;
        }
    }

    return false;
}

/**
 * Given a list of file paths (eg ["slideshow/ext_f_decking.png", "slideshow/rm_l_fireplace.png"])
 * Return the list ordered by the property part order specified in getOrder above.
 * File paths with no property part key are placed at the end of the list
 *
 * @param string[]
 *
 * @return string[]
 */
function sortImages($paths)
{
    // Associate each property part key with an ascending number
    $order = array_flip(getOrder());

    usort($paths, function ($a, $b) use ($order) {
        $a_property_part_key = getproperty_partFromFilePath($a);
        $b_property_part_key = getproperty_partFromFilePath($b);

        // If $a has no property part key, it comes last
        if (!$a_property_part_key) {
            return 1;
        }

        // If $b has no property part key, it comes last
        if (!$b_property_part_key) {
            return -1;
        }

        // If both images are of the same part of the property, order them naturally
        if ($a_property_part_key == $b_property_part_key) {
            return $a <=> $b;
        }

        // If the property part keys are different, the earliest one in getOrder() comes first
        return $order[$a_property_part_key] - $order[$b_property_part_key];
    });

    return $paths;
}
Now you can replace:

Code: Select all

foreach(glob(IMAGEPATH_15."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE) as $filename_15){
with:

Code: Select all

foreach(sortImages(glob(IMAGEPATH_15."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE) as $filename_15)){
and your images will be in the order you specify in the function getOrder at the top of this code.

It's worth noting that you code is starting to get pretty convoluted and difficult to read here. PHP was originally written as a templating language and it behaves well when used as such. Have a read up on templating and see if would help you; https://css-tricks.com/php-is-a-ok-for-templating/ seems like a decent introduction.
guvna
php-forum Active User
php-forum Active User
Posts: 34
Joined: Fri Mar 10, 2017 7:42 am

Mon Oct 26, 2020 4:53 am

Thanks for your comments, they're much appreciated as I'm a business owner rather than a coder. I will try a version implementing your solution.

I did stumble on a solution that actually works (shock/horror), so would appreciate further comments on whether the code is acceptable or any reasons why it shouldn't be done this particular way.

This is the code that gets the images and sorts them as required using array_push() method. It is located in an include file so can be called whenever required:

Code: Select all

// Define the function to get all the images in the current folder:
function getImages(){
    foreach(glob(IMAGEPATH."*.{jpg,png,gif,JPG,PNG,GIF}", GLOB_BRACE) as $var){
        $img_src = basename($var);
        
        // Assign specific values to the array as you cycle through the collection. Order here will not affect final array order.
        if (substr($img_src, 0, 5) == 'bed_1'){
            if (!isset($arrayBed_1)) {
                $arrayBed_1 = array();
            }
            $arrayBed_1[] = $img_src;
        } elseif (substr($img_src, 0, 5) == 'bed_2'){
            if (!isset($arrayBed_2)) {
                $arrayBed_2 = array();
            }
            $arrayBed_2[] = $img_src;
        } elseif (substr($img_src, 0, 5) == 'bed_3'){
            if (!isset($arrayBed_3)) {
                $arrayBed_3 = array();
            }
            $arrayBed_3[] = $img_src;
        // continue for each type required.
        }           }
    } //End of foreach().
    
    // Create the pre final array for other arrays to push to which defines the sort order:
    $arrayPreFinal = array();
    
    if (isset($arrayExt_f)){                        // ext_f = exterior front
        foreach ($arrayExt_f as $val){
            array_push($arrayPreFinal, $val);
        }
    }
    if (isset($arrayExt_r)){                        // ext_r = exterior rear
        foreach ($arrayExt_r as $val){
            array_push($arrayPreFinal, $val);
        }
    }
    if (isset($arrayBed_1)){                        // bed_1 = bedroom 1
        foreach ($arrayBed_1 as $val){
            array_push($arrayPreFinal, $val);
        }
    }
    if (isset($arrayBed_2)){                        // bed_2 = bedroom 2
        foreach ($arrayBed_2 as $val){
            array_push($arrayPreFinal, $val);
        }
    }
    // continue for as many variances as require.
    
    
    return $arrayPreFinal;
} // End of function()
This is the code that runs from folders where the images are actually located:

Code: Select all

// Set $iwd Image Working Directory:
// Required before gallery_ctrl_body.php inlcude as $iwd is needed before include.

// class IWD changes outside the class.
class IWD {
    // Properties
    public $iwd;
}
$var = new IWD();
$var->name = getcwd();
$iwd = $var->name;

// Script include needs to be run before function pid_text().
include_once ('../gallery_ctrl_body.php');

# Function to identify the property sub-division from directory path characters using preg_match()
# and define a Property ID Text:
function pid_text($sub_div) {       //Need parameter/s in function():
    $sub_div_abb = sub_div_abb($sub_div); //20200220 code
    $psc = 'ORL'; // Start of Property System Code.
    $str = getcwd();

    if(preg_match("/{$sub_div}/i", $str)) {
        $psc = $psc . $sub_div_abb . strtoupper(basename(__DIR__)); //sub_div_abb may already defined in UPPERCASE.
        $pid_textname = constant("PROPERTY_TEXT") . $psc; //strtoupper($psc);
    }
    return $pid_textname;                   //Return value AFTER IF statement. REQUIRED!
}   
$pid_text = pid_text($sub_div = 'test_200828');
    
// Define the imagepath and variables:
define('IMAGEPATH', dirname(__FILE__).'/'); // '/' is required.
$img_height = 'height: 400px'; // default image height for gallery.
$img_width = 'width: 600px'; // default image width for gallery.
$img_counter = 0;
$img_style = 'style="' .  $img_width . '"'; // only needs to be set once here.
$img_total = 0;

// Create the final array with function call to get images:
$arrayFinal = getImages();

// Calculate total number of images before displaying <div>
// If calculated inside a foreach() $img_counter always equals $img_total.
$img_total = count($arrayFinal);

echo '<div class="property_text-container">';
    echo 'Gallery for ' . $pid_text;
echo '</div>';

//<!-- Slideshow container -->
echo '<div class="slideshow-container">';

foreach ($arrayFinal as $value){
    $img_counter ++; // Set before function() when using function call.
    // Call the function located in gallery_ctrl_body:
    createGallery($img_width, $img_style, $filename, $img_name, $img_src, $img_counter, $img_total, $value, $pid_text);
}

// <!-- Next and previous buttons -->
echo '<a class="prev" onclick="plusSlides(-1)">&#10094;</a>';
echo '<a class="next" onclick="plusSlides(1)">&#10095;</a> ';

echo '</div>';
// <!-- END: div class="slideshow-container"-->
echo '<br>';

// <!-- Dot container -->
echo '<div class="dot-container">';
    // <!-- The dots/circles -->
    echo '<div style="text-align:center">';
        $slide_counter = 0;
        while ($slide_counter < $img_total) {
            $slide_counter ++;
            echo '<span class="dot" onclick="currentSlide(' . $slide_counter . ')"></span>&nbsp;';
        }
    echo '</div>';
echo '</div>';

// Script include needs to be run at end of page including it.
include_once ('../gallery_ctrl_script.php');


Once again, any comments, whether positive or negative, will be greatly appreciated.
simonbrahan
php-forum Fan User
php-forum Fan User
Posts: 90
Joined: Mon Jun 08, 2020 2:00 am
Contact:

Wed Oct 28, 2020 1:53 pm

I can see how that would work. Not how I'd do it, but if it works...
Post Reply