Skip to content
Advertisement

Move 2nd level sub-array to the top of the 1st level multidimentional array based on value of the sub-array

I’m looping through a multidimensional array and am left with some values.

This is the complete PHP code.

<?php 
/**
 * Each designer is required 3x per week, 
 * add their name 3 times to allow for the count.
 * 
 * [array_pop]  removes the designer from the sub-array
 * u[sort($designers, "sortByTotalItems")] then moves the designers with more names remaining to the top of the list
 *
 * @var [type]
 */
$designers = array(
    [ "Aaron Summers", "Aaron Summers", "Aaron Summers" ],
    [ "Adam Smart", "Adam Smart", "Adam Smart" ],
    [ "Andrew Montgomery", "Andrew Montgomery", "Andrew Montgomery" ],
    [ "Dave Robertson", "Dave Robertson", "Dave Robertson" ],
    [ "Holly Colby", "Holly Colby", "Holly Colby" ],
    [ "Karen Fok", "Karen Fok", "Karen Fok" ],
    [ "Kieran Lintott", "Kieran Lintott", "Kieran Lintott" ],
    [ "Rebecca Roffey", "Rebecca Roffey", "Rebecca Roffey" ],
    [ "Sonia Tam", "Sonia Tam", "Sonia Tam" ],
    [ "Steffi Wallace", "Steffi Wallace", "Steffi Wallace" ],
    [ "Taz Patel", "Taz Patel", "Taz Patel" ]
);
/**
 * Store the original designers to reset them once empty
 *
 * @var [type]
 */
$designers_bak = $designers;
/**
 * Generate an array of string dates between 2 dates
 *
 * @param string $start Start date
 * @param string $end End date
 * @param string $format Output format (Default: Y-m-d)
 *
 * @return array
 */
function getDatesFromRange( $start, $end ) {
    $array = array();
    $interval = new DateInterval('P1D');

    $realEnd = new DateTime($end);
    $realEnd->add($interval);

    $period = new DatePeriod(new DateTime($start), $interval, $realEnd);
    
    foreach($period as $date) { 
        $dayOfWeek = $date->format('l');
        if ( $dayOfWeek != 'Saturday' && $dayOfWeek != 'Sunday' ) {
            $array[] = $date->format('Y-m-d'); 
        }
    }

    return $array;
}
/**
 * Sort the array into sub-arrays that contain the most values
 * array(
 *     array('name', 'name', 'name')
 *     array('name', 'name')
 *     array('name')
 * )
 * https://stackoverflow.com/questions/7433569/php-sort-a-multidimensional-array-by-number-of-items#answer-7433753
 */
function sortByTotalItems($a, $b) {
    if ($a == $b) {
        return 0;
    }
    return (count($a) > count($b)) ? -1 : 1;
}
/**
 * Get the total amount of days in this week 1-5
 *
 * @var [type]
 */
$days_at_work = 3;
$total_days_in_week = 5;
$assigned_total = floor(count( $designers ) * $days_at_work / $total_days_in_week);    

/**
 * Each day
 *
 * @var [type]
 */
$working_weeks = array_chunk(getDatesFromRange('09-06-2021', '09-06-2022'), $total_days_in_week);
$given_designer = array();
foreach ( $working_weeks as $working_days ) {
    /**
     * Reset the designers
     *
     * @var [type]
     */
    foreach ( $working_days as $working_day ) {

        /**
         * Randomise the designers
         *
         * @var [type]
         */
        shuffle( $designers ); 
        /**
         * Sort the shuffle, so the sub-arrays count is descending
         * array(
         *     array('name', 'name', 'name')
         *     array('name', 'name')
         *     array('name')
         * )
         *
         * @var [$designers]
         */
        $designers = array_filter(array_map('array_filter', $designers));
        usort($designers, "sortByTotalItems");



        /**
         * Add designer to the working day
         *
         * @var [type]
         */

        $forcount = ( count($designers) < $assigned_total ) ? count($designers) : $assigned_total;



        for ($d=0; $d < $forcount; $d++) {
            /**
             * Add designer to the days visit
             *
             * @var [type]
             */

            $given_designer[] = array( 
                                    "title"=> $designers[$d][0],
                                    "start"=> $working_day
                                );
            /**
             * Remove the name from the names array -1 each loop
             *
             * @var [type]
             */
            array_pop($designers[$d]);
            /**
             * remove empty arrays so we can count the remaining designers
             * If there are less than 1 weeks worth of designers, reset the variable
             */
            $current_designers = array_filter(array_map('array_filter', $designers));
            if (  count($current_designers) < $assigned_total ) {
                $designers = $designers_bak;
            }
        }

        
    }  
}
?>

Below is what I’m left with after the loops and array_pop.

Let’s call this array1

<?php 
array ( 0 => array ( ),
        1 => array ( ),
        2 => array ( ),
        3 => array ( ),
        4 => array ( 0 => 'Aaron Summers', ),
        5 => array ( 0 => 'Taz Patel', ),
        6 => array ( 0 => 'Sonia Tam', ),
        7 => array ( 0 => 'Adam Smart', ),
        8 => array ( 0 => 'Andrew Montgomery', ),
    ) 
?>

I then reset the array with the original values:

Let’s call this array2

<?php
array ( 0 => array ( 0 => 'Aaron Summers', 1 => 'Aaron Summers', 2 => 'Aaron Summers', ),
        1 => array ( 0 => 'Adam Smart', 1 => 'Adam Smart', 2 => 'Adam Smart', ),
        2 => array ( 0 => 'Andrew Montgomery', 1 => 'Andrew Montgomery', 2 => 'Andrew Montgomery', ),
        3 => array ( 0 => 'Dave Robertson', 1 => 'Dave Robertson', 2 => 'Dave Robertson', ),
        4 => array ( 0 => 'Holly Colby', 1 => 'Holly Colby', ),
        5 => array ( 0 => 'Karen Fok', 1 => 'Karen Fok', 2 => 'Karen Fok', ),
        6 => array ( 0 => 'Kieran Lintott', 1 => 'Kieran Lintott', 2 => 'Kieran Lintott', ),
        7 => array ( 0 => 'Rebecca Roffey', 1 => 'Rebecca Roffey', 2 => 'Rebecca Roffey', ),
        8 => array ( 0 => 'Sonia Tam', 1 => 'Sonia Tam', 2 => 'Sonia Tam', ),
        9 => array ( 0 => 'Steffi Wallace', 1 => 'Steffi Wallace', 2 => 'Steffi Wallace', ),
        10 => array ( 0 => 'Taz Patel', 1 => 'Taz Patel', 2 => 'Taz Patel', ),
    )
?>

How would I be able to re-order the items in array2 based on the people left in array1 ?

array2 is the total size of the array and the array in its original form, before I have looped through and used array_pop() to remove the name.

Advertisement

Answer

There are a couple of ways to do it, this being fairly simple since the names should be unique anyway:

$array2 = array_merge(array_column(array_filter($array1), null, 0), 
                      array_column($array2, null, 0));
  • Remove empties from the ordered array $array1, or do this to the final result after * see below
  • Index each array on the value, which should at least be at index 0
  • Merge the existing array into the ordered one, which will replace in the proper order and add the remaining names to the end

If you really need to get integer indexes then use array_values:

$array2 = array_values(array_filter(array_merge(array_column($array1, null, 0),
                                                array_column($array2, null, 0)));

Whatever you are doing with all of that code could probably be done much shorter and simpler if you use the name as the index in the first place.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement