PHP Functions – Part 2



Thursday, October 11th 2012

Pad, Reduce, Chunk

I lumped these three functions together because I doubt I could count on one hand the number of times I’ve used any of them. Though once you understand what they do there are several applications for them that would make development cleaner if not also just plain easier. Here they are in the order I’m using them in the forthcoming example:

  • array_pad – Pads an array to the specified size with the specified value
  • array_reduce – Applies a function iteratively to the elements of the array so as to reduce the array to a single value.
  • array_chunk – Splits an array into chunks of the specified size

For a semi-useful example, we’re going to run through these three functions in a script that gathers the top 20 scores for display on a page. Find the full example at the bottom of this post, I’m going to explain all the parts individually first.

array_pad

// Pad the resulting array up to 20
$top_20 = array_pad( $db_result, 20, FALSE );

In our example we need to be able to display 20 results, it’s not much of a top 20 without 20 results right? So we’re going to use array_pad, this will fill the array with 20 items no matter the number of results. The first parameter is the array we need to pad (the $db_result in the example), second is the pad size (20 in the example), finally the third parameter is the value used to pad. Though not used here, we could also pad to the beginning of the array by making the second parameter a negative number, also the third parameter is not restricted to a boolean and can be a number, string, array, etc.

array_reduce

function r_scoresum ( $sum, $player ) {
    if ( $player !== FALSE ) {
        $sum += $player['score'];
    }
    return $sum;
}
// Gather total score of all players in the top 20
$top_20_total = array_reduce( $top_20, "r_scoresum" );

The two snippets above determine the sum of the returned scores using array_reduce. Array_reduce requires two parameters, the array to reduce and the name of a callback function to use to perform said reduction. There’s an optional third parameter called initial that is used at the beginning of the process or as a final result in case the array is empty. The function here makes sure the array item passed to the r_scoresum function is not set to FALSE (one of the padded items that are possibly added at the end of the array) before adding the score to the running total. $top_20_total will now contain the integer 1080898.

function r_output ( $output, $player ) {
    if ( $player === FALSE ) {
        $output .= "\t".'<li>[empty]</li>'."\n";
    } else {
        $output .= "\t".'<li><a href="#">'. $player['name'] .' - '. number_format( $player['score'] ) .'</a></li>'."\n";
    }
    return $output;
}
// Set up a simple page of output for the top players
$top_20_output = array_reduce( $page, "r_output" );

This is another use of the array_reduce function, I set up a function r_output that takes the array items and produces some HTML I then use to display the data in a more readable format.

array_chunk

// Paginate the results
$top_20_paginated = array_chunk($top_20, 10);

array_chunk splits an array into a multidimensional array whose parent items contain arrays of the specified size. This is an example of a possible use of array_chunk, we’re using it as a simple method for paginating results. I wanted ten items per page so I set array_chunk with the array to chunk and the second parameter was set to 10. So now $top_20_paginated will contain an two-dimensional array with two values, each of these two values will themselves be an array of ten results each.

Full Example

Here’s the code in it’s entirety:

<?php
/**
 * Reduce the $player array to the sum of all returned scores
 * @param int $sum - Stored sum of all player scores
 * @param array $player - Array of individual player data
 * @return int - The total sum of all provided player scores
 */
function r_scoresum ( $sum, $player ) {
    if ( $player !== FALSE ) {
        $sum += $player['score'];
    }
    return $sum;
}
/**
 * Return a simple output of the players and their scores linking elsewhere
 * @param string $output
 * @param array $player - Array of individual player data
 * @return string - The ordered list items of the player scores
 */
function r_output ( $output, $player ) {
    if ( $player === FALSE ) {
        $output .= "\t".'<li>[empty]</li>'."\n";
    } else {
        $output .= "\t".'<li><a href="#">'. $player['name'] .' - '. number_format( $player['score'] ) .'</a></li>'."\n";
    }
    return $output;
}

// Faux Database results
$db_result = array(
    array( 'name' => 'Nelson Stryker', 'score' => 66581 ),
    array( 'name' => 'Fernando Cuneo', 'score' => 65897 ),
    array( 'name' => 'Darryl Gudger', 'score' => 64152 ),
    array( 'name' => 'Lance Secrist', 'score' => 63652 ),
    array( 'name' => 'Christian Rostad', 'score' => 62800 ),
    array( 'name' => 'Nelson Wantz', 'score' => 61896 ),
    array( 'name' => 'Dona Hirata', 'score' => 61688 ),
    array( 'name' => 'Marcia Kyser', 'score' => 60161 ),
    array( 'name' => 'Julio Nordberg', 'score' => 59093 ),
    array( 'name' => 'Maricela Gapinski', 'score' => 58780 ),
    array( 'name' => 'Adrienne Carreon', 'score' => 58702 ),
    array( 'name' => 'Nelson Rearick', 'score' => 58326 ),
    array( 'name' => 'Ericka Ursery', 'score' => 58101 ),
    array( 'name' => 'Earlene Carstens', 'score' => 57684 ),
    array( 'name' => 'Sofia Regal', 'score' => 57047 ),
    array( 'name' => 'Katy Unknow', 'score' => 56453 ),
    array( 'name' => 'Lance Weibel', 'score' => 55362 ),
    array( 'name' => 'Clare Deppen', 'score' => 54523 )
);
// Count of original database results
$db_result_count = count( $db_result );

// Pad the resulting array up to 20
$top_20 = array_pad( $db_result, 20, FALSE );
// Gather total score of all players in the top 20
$top_20_total = array_reduce( $top_20, "r_scoresum" );
// Paginate the results
$top_20_paginated = array_chunk($top_20, 10);

//After pagination is determined, set up an ordered list to display
foreach ( $top_20_paginated as $pagenum => $page ){
    // Set up a simple page of output for the top players
    $top_20_output = array_reduce( $page, "r_output" );
    echo '<h2>Page '. ( $pagenum + 1 ) .'</h2>'."\n".
        '<ol start="'. ( $pagenum * 10  + 1 ) .'">'."\n". $top_20_output .'</ol>'."\n";
}

// General stats
echo '
<dl>
    <dt>Sum of the Top 20 scores</dt><dd>'. number_format( $top_20_total ) .' points</dd>
    <dt>Average of the Top 20 scores</dt><dd>'. number_format( round( $top_20_total / $db_result_count ) ) .' points</dd>
</dl>';
?>

And the output will look like this:

<h2>Page 1</h2>
<ol start="1">
    <li><a href="#">Nelson Stryker - 66,581</a></li>
    <li><a href="#">Fernando Cuneo - 65,897</a></li>
    <li><a href="#">Darryl Gudger - 64,152</a></li>
    <li><a href="#">Lance Secrist - 63,652</a></li>
    <li><a href="#">Christian Rostad - 62,800</a></li>
    <li><a href="#">Nelson Wantz - 61,896</a></li>
    <li><a href="#">Dona Hirata - 61,688</a></li>
    <li><a href="#">Marcia Kyser - 60,161</a></li>
    <li><a href="#">Julio Nordberg - 59,093</a></li>
    <li><a href="#">Maricela Gapinski - 58,780</a></li>
</ol>
<h2>Page 2</h2>
<ol start="11">
    <li><a href="#">Adrienne Carreon - 58,702</a></li>
    <li><a href="#">Nelson Rearick - 58,326</a></li>
    <li><a href="#">Ericka Ursery - 58,101</a></li>
    <li><a href="#">Earlene Carstens - 57,684</a></li>
    <li><a href="#">Sofia Regal - 57,047</a></li>
    <li><a href="#">Katy Unknow - 56,453</a></li>
    <li><a href="#">Lance Weibel - 55,362</a></li>
    <li><a href="#">Clare Deppen - 54,523</a></li>
    <li>[empty]</li>
    <li>[empty]</li>
</ol>

<dl>
    <dt>Sum of the Top 20 scores</dt><dd>1,080,898 points</dd>
    <dt>Average of the Top 20 scores</dt><dd>60,050 points</dd>
</dl>