Array Handling Functions

In my previous article on PHP arrays I suggested a number of things that are tables and therefore can also be expressed as arrays. In this article I’ll use a pack of playing cards to explore some of the built-in array functions most often needed by PHP programmers.

To highlight some of the array-handling functions PHP offers, I’ll be using some components of Buraco – a game very popular in my part of the world and quite similar to Rummy. Real-world Buraco is played with two decks (104 cards) plus two Joker cards. It also has a stock pile that holds all cards not dealt to the players, but I won’t use them here so you needn’t worry about them.

Representing a Deck of Cards

Cards date back possibly to the ninth century, the time when sheets of paper first began to be used in China. They followed the path of other inventions from the East – first to the Arab world from where they were then taken to Europe and later into the New World. In its current most popular form, the French Pack, a deck of playing cards has 52 cards divided in four suits, clubs (♣), diamonds (♦), hearts (♥) and spades (♠). Each suit has 13 cards, or faces: A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q and K.

You can write arrays to hold both suits and faces like this:

<?php
$suits = array ("clubs", "diamonds", "hearts", "spades");
$faces = array (1 => "A", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13");

Both are numerically indexed arrays – that is, they have integer-based keys. Because I didn’t explicitly give any keys when I defined $suits, PHP automatically assigns keys to them starting with 0. So, the value of $suits[0] is “clubs” and $suits[3] is “spades”. I did however provide a key to the first element of $faces. PHP assigns each new key by taking the the maximum integer index and adding 1 to it. $faces[1] is “A”, $faces[2] is “02”, $faces[3] is “03” and so on. You’ll notice I forced PHP to start indexing $faces with 1 so the numerical card faces are identical to their respective keys.

You can use two foreach loop to create a master array of all 52 cards, each represented as a string in the format face|suit, like this:

<?php
$deck = array();
foreach ($suits as $suit) {
    foreach ($faces as $face) {
        $deck[] = $face . "|" . $suit;
    }
}

The result of the above code is the same as if you had populated $deck by hand:

<?php
$deck = array("A|clubs", "02|clubs", "03|clubs", "04|clubs", ... "Q|spades", "K|spades");

Some of the more experienced readers may be asking why not use a nested array as opposed to strings, such as:

<?php
$deck = array();
$deck["A"] = array("clubs", "diamonds", "hearts", "spades");
$deck["02"] = array("clubs", "diamonds", "hearts", "spades");
$deck["03"] = array("clubs", "diamonds", "hearts", "spades");
...

Well, that’s the beauty of it: strings can sometimes be treated as non-associative, single level arrays but arrays nonetheless! In fact, the same function used to count the number of elements in an array – count() – can also be used to count the number of characters in a string! Later you’ll see how to convert strings into arrays.

Dealing a Hand

Let’s begin by shuffling the deck and dealing out a hand of 11 random cards. To do this, you can use the function array_rand(). It returns an array of keys taken randomly from the original. The function was designed to return keys rather than values so it can work just as efficiently with nested arrays as with single-level arrays, and you can always obtain the values if you know the key.

<?php
$myKeys = array_rand($deck, 11);
$myHand = array();
foreach ($myKeys as $key) {
    $myHand[] = $deck[$key];
    unset($deck[$key]);
}

Initially, the temporary $myKeys array is created whose values are 11 random keys found in $deck. Then a foreach loop uses the value of $myKeys to get the corresponding value from $deck into $myHand. Of course this does not remove elements from the original deck. If you were to call array_rand() again, it is entirely possible you could get a few keys indexing again some cards that had already been drawn! To make sure this doesn’t happen, unset() is called to delete the element from $deck to make sure it can’t be reused.

To find out whether a card, say “06|hearts” (6♥), is in the hand that was dealt, you can use the in_array() function. It accepts a needle first, the desired value to search for, and then the haystack, the array in which to search.

<?php
if (in_array("06|hearts", $myHand)) {
    echo "I found it!";
}

As a side note about needles and haystacks, evangelists of other languages love to find faults with PHP (and of course the reverse is also true!). The only criticism I could never refute was PHP’s irritating inconsistent parameter order between similar functions. Some functions, like in_array(), accept the needle first, while other functions accept the haystack first. I know long-time PHP developers who still have trouble remembering which order some functions use, so don’t be too put off if you find yourself always needing to check the online documentation.

in_array() returns only whether something was found in an array, not the key of the value. Most of the time this is sufficient. But if you need to know the key as well, consider using array_search().

Good housekeeping is important, and thanks to the face|suit manner in which the cards are represented, sorting them is as easy as using sort(). This function orders the elements of the array in ascending alphabetical and numeric order:

<?php
sort($myHand);

The sort() function is peculiar in that it operates on its own argument! If you wanted to preserve the original order of $myHand you would have to copy it to another variable before sorting:

<?php
$preservedHand = $myHand;
sort($myHand);

Melds and Discards

Buraco, like rummy, is a game of runs – sequences laid down on the table. The act of removing cards from the hand onto a run is called melding. For example, cards 9♦, 10♦, J♦ and Q♦ if they were in $myHand could be melded to make a run. Programatically this means removing the cards from $myHand and putting them into a new array, $myRuns. You can use the function array_slice() which returns a copy of a range of elements from an array, very much like the way substr() works on strings. Assuming the sequences occupies positions 0 through 3 in $myHand:

<?php
$myRuns = array();
$myRuns[] = array_slice ($myHand, 0, 4);
$myHand = array_slice($myHand, 5, 7);

The first line creates the array $myRuns and the second one adds to it a sub-array composed of 4 elements from $myHand beginning at index 0, which is the first position in the array. array_slice() does not delete the slice from the original array, which is a problem here since you need to remove the cards from your hand that you meld or discard. The solution here is to use array_slice() a second time to reassign the other elements of $myHand to itself.

At the beginning of his turn, a player must either pick up one card from the stock or all cards from the discard pile and add them to his hand. Both options can be done using the array_merge() function. The function returns an array composed of all the elements from the given arrays in the order they were provided. For example, let’s say the state of the game looks like this:

Player’s Hand: 2♣, 5♥, 6♠, 6♥, 7♦, 8♥, K♠
Run: 9♦, 10♦, J♦, Q♦
Discard Pile: 7♠, J♠, 4♥, 4♦

To pick up all the cards in the discard pile and add them to the players hand, you could write:

<?php
$myHand = array_merge($myHand, $discarded);

Afterwards, $myHand would contain:

array("02|clubs", "5|hearts", "06|spades", "06|hearts", "07|diamonds", "08|hearts", "13|spades", "07|spades", "11|spades", "04|hearts", "04|diamonds")

Showing the Hand

It’d be nice to display one or more cards in the browser. My strategy is to use the face|suit notation to create a number of HTML image tags that will show face and suit graphics. While it is not likely an effective solution for a real-world application, it does afford me an opportunity to show you one last function, explode().

<?php
foreach ($myHand as $card) {
    $tmpArray = explode("|", $card);
    echo '<img src="img/face_' . $faces[$tmpArray[0]] . '.png">";
    echo '<img src="img/suit_' . $suits[$tmpArray[1]] . '.png">";
    echo "&nbsp;"; // Just a blank space for visual separation 
}

The foreach loops through all of the cards in $myHand and returns one face|suit value each iteration as $card. The explode() function takes each $card value and splits it into two pieces using the vertical bar as the separator. It then returns an array with as many elements as there are parts. I’m using a vertical bar here, but explode() will work with any character or sequence of characters.

Summary

PHP has over 70 array-related functions. In this tutorial you’ve seen how to use a mere handful of them to perform some basic array manipulations. These functions are tools you’ll use often to solve programming problem or, as I prefer to call them, challenges. It’s important to develop a familiarity with them so that when you plan your script you can make sound decisions based on the results you need.

Image via Cindy Haggerty / Shutterstock

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • http://gilbert.im/ Gilberto Ramos

    Hola!
    I just want to give my 2 cents and point out a little mistake in the use of the explode() function! The first parameter is the delimiter and then comes the string you are splitting.

    explode ( string $delimiter , string $string [, int $limit ] )

    In the last example you inverted the order.
    Thanks!

    • http://zaemis.blogspot.com Timothy Boronczyk

      You’re right, Gilberto. explode() does indeed take the delimiter first, and then the string to split. I fixed the example in the article so others won’t get tripped up on it. Thanks!

  • J A Jeronymo

    My mistake! Sorry!
    ¡Gracias, Gilberto!

  • http://www.cleotech.fr Renan RODRIGUES

    Simply thank’s for this post ! It was helpfull.
    Greetings from France

    • J A Jeronymo

      Pas de quoi Renan! Très sympa!