Copter Labs Copter Labs

Smart Design.

For Smart People.

Hold on... This isn't EnnuiDesign.com — What Gives?

It's been a long time coming, but Jason Lengstorf, formerly of Ennui Design, has expanded his team to include Drew Douglass, Rob MacKay, Henry Moran, and Tom Sturge.

It didn't feel right to keep the same name, so we decided to continue on as Copter Labs. You can expect the same great content under this new name!

Generate a Random Subset of Array Elements

I ran into an interesting problem while developing a new blogging system recently where I needed to generate a subset of array elements in random order.

A random subset of elements is useful for displaying a sampling of entries that changes with every page load, such as four random blog entries, a photo gallery that stays interesting even if new photos aren't uploaded, or, if you want to use a sweet content slider, you could show random portfolio or blog entry previews and keep your slider fresh.

Defining the Problem

The Array

First things first, we need to know what we're dealing with. For the purpose of this example, we'll be using a multi-dimensional array that looks something like this:

$entries = array(
	array(
		'title' => 'Entry One',
		'author' => 'Jason Lengstorf',
		'date' => 'April 8, 2009'
	),
	array(
		'title' => 'Entry Two',
		'author' => 'Jason Lengstorf',
		'date' => 'April 9, 2009'
	),
	array(
		'title' => 'Entry Three',
		'author' => 'Jason Lengstorf',
		'date' => 'April 10, 2009'
	),
	array(
		'title' => 'Entry Four',
		'author' => 'Jason Lengstorf',
		'date' => 'April 11, 2009'
	),
	array(
		'title' => 'Entry Five',
		'author' => 'Jason Lengstorf',
		'date' => 'April 12, 2009'
	),
	array(
		'title' => 'Entry Six',
		'author' => 'Jason Lengstorf',
		'date' => 'April 13, 2009'
	),
	array(
		'title' => 'Entry Seven',
		'author' => 'Jason Lengstorf',
		'date' => 'April 14, 2009'
	),
	array(
		'title' => 'Entry Eight',
		'author' => 'Jason Lengstorf',
		'date' => 'April 15, 2009'
	)
);

This is an array that's similar to the kind that would be returned from a database query. In it, we have eight entries, each of which contains another array that holds the title, author, and date of the entry (I omitted the entry itself in the interest of brevity).

The Goal

Our goal is to create a subset of four entries in random order, and to have the subset change every time the page is loaded (i.e. entries 4, 2, 8, and 7 are loaded the first time, then entries 3, 8, 1, and 6, and so on).

Writing the Code

Our array will be processed in a function called getRandomSubset(), which will accept two arguments: the array ($entries), and the number of entries to be returned in the subset ($number).

function getRandomSubset($entries, $number)
{
	// Process entries here...
}

PHP provides a ton of array handling functions that simplify the process of manipulating arrays. One of the available functions is called array_rand(), which returns a random assortment of array keys (as a variable if only one element is returned, or as an array if two or more are returned).

We're able to pass two arguments to array_rand(): the first is required and contains the array from which we need random elements, and the second is an optional specifier that allows us to decide how many elements need to be returned (if not set, this defaults to 1).

Because array_rand() only returns the array element keys, we'll need to do a little more coding to get the actual element values. To do this, we'll create an empty array called $randList that will store our random array elements, then run a loop with the random keys to add each element to the array using another array function called array_push(), which adds a value to the end of an array.

function getRandomSubset($entries, $n)
{
	// Initiate the variable
	$randList = array();

	// Pull the desired number of entry keys at random
	$keys = array_rand($entries, $n);

	// Loop through the keys
	foreach($keys as $key) {

		// Add the value to end of the return array
		array_push($randList, $entries[$key]);

	}

	return $randList;
}

To test this code, add the following snippet to a test page. NOTE: The <pre> tags are used to enhance the readability of output generated by print_r().

echo '<pre>';
print_r(getRandomSubset($entries, 2));
echo '</pre>';

Running the code will output something similar to the following:

Array
(
    [0] => Array
        (
            [title] => Entry Four
            [author] => Jason Lengstorf
            [date] => April 11, 2009
        )

    [1] => Array
        (
            [title] => Entry Six
            [author] => Jason Lengstorf
            [date] => April 13, 2009
        )

)

The Problem with Too Few Entries

One problem that exists in our code is that a warning will be issued and no values returned if more entries are requested than available in our entry array. If we call echo getRandomSubset($entries, 9);, an error similar to the following will be output:

Warning: array_rand() [function.array-rand]: Second argument has to be between 1 and the number of elements in the array in /Applications/xampp/xamppfiles/htdocs/test.php on line 75

Warning: Invalid argument supplied for foreach() in /Applications/xampp/xamppfiles/htdocs/test.php on line 78
Array ( )

Solving the Problem

To correct this issue, we need to add a check that ensures the number of entries requested does not exceed the number supplied. If too many entries are requested, we then change the number of requested entries to match the number of available entries, thus avoiding the error and outputting as many entries as possible.

function getRandomSubset($entries, $n)
{
	/*
	 * If $n is greater than the number of entries,
	 * reset it to be the number of entries available.
	 */
	$n = (count($entries)<$n) ? count($entries) : $n;

	// Initiate the variable
	$randList = array();

	// Pull the desired number of entry keys at random
	$keys = array_rand($entries, $n);

	// Loop through the keys
	foreach($keys as $key) {

		// Add the value to end of the return array
		array_push($randList, $entries[$key]);

	}

	return $randList;
}

Testing the function should now always result in a subset of the desired length with no duplicate entries. The subset can then be passed to the formatting function of your choice to handle formatting.

Summary

Getting comfortable with the intricacies of loops and arrays can have a huge impact on the cleanliness and readability of your code. What tricks do you have up your sleeve? Let me know in the comments!

Other Notes

I had an article run on NETTUTS called Add Power to Your PHP With Multi-Tiered Applications. It covers what I consider to be best practices while writing code, so I'd love to hear your thoughts on it.

Also, just as a general note, thanks for reading! I really appreciate your comments and support, and I hope to keep supplying you with all the PHP geekery you can handle!

Date. 04/21/2009

Comments. 9

Category. PHP

Was This Post Helpful? Pass It On!

Share the Love

If this post taught you something, reminded you of something you had forgotten, or just made you feel good, there's really no better way to say "thank you" than passing it along to your friends.

Don't forget to like us on Facebook, join our newsletter, and/or subscribe to our RSS feed to make sure you hear about new posts first!

Join Our Gaggle of Geeks
* indicates required

Comments.

  1. Gravatar

    Why didn't you use mt_rand()? I would use it if I had to write something like this.

  2. Gravatar

    @Frank:

    In this case, because it doesn't really matter how random the entries are, I opted for the built-in function to pull random array keys.

    I had originally written another way to do this that used mt_rand(), but it was more complicated.

    However, I'd love to see your take on the function. Feel free to post your version in the comments!

  3. Gravatar

    @Jason Lengstorf:

    Oh, I overlooked the array_rand() function. I think that's a better solution than mt_rand(). :)

    But I would do something like this: http://pastebin.com/f271df581

  4. Gravatar

    @Frank:

    That's almost exactly what my original solution was, except I skipped the while(true) by leaving the third expression in the for loop empty:

    function get_random_subset($array, $n)

    {

    $random_items = $random_keys = array();

    $item_count = count($array);

    $n = $n > $item_count ? $item_count : $n;

    for ($x=0; $x < $n; ) {

    $r = mt_rand(0, $item_count-1);



    if (!in_array($random_items, $array[$r])) {

    $random_items[] = $array[$r];

    ++$x;

    }

    }



    return $random_items;

    }

    Thanks for your input!

  5. Gravatar

    Also came across this problem once, but I wanted to preserve the keys of the original array, so I made this oneliner:

    function array_rsubset($array, $count) {

    return array_intersect_keys($array, array_flip(array_rand($array, $count)));

    }

  6. Gravatar

    @Rudolf Leermakers:

    That function is brilliant! However, the entries are always in ascending order, so you'd have to write another function to shuffle them.

    Also, array_intersect_keys() isn't actually a function; I think you were looking for array_intersect_key().

    Thanks!

  7. Gravatar

    @Jason

    Yeah, the intersect_keys() was a typo, I didn't actually copy-paste it, didn't take the time to find where I stored it :)

    And for the order-problem, that has to do with the order of the original array, you could shuffle that, or shuffle the end-result in the same line, but I totally missed that problem before, thanks for mentioning it!

  8. Gravatar

    Nice to be visiting your blog again Herve Leger, it has been months for me. Well this article that i’ve been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article Herve Leger Dresses. Thanks, great share.

  9. Gravatar

    Choosing amongst a myriad of large things was not in the least my speciality, so when I had to preferable my exceed ten computer games, I was definitely faced with a challenge. After some pensive (and I mean surely judgement sometimes non-standard due to), this is the file of games that I know are worthy to be mentioned in my top ten computer games article.



    Lately, many people the time of one's life playing video games on consoles such as Ps3 and Xbox 360 because of alluring games and the ease of use. Many consoles involve with a outstrip graphics card that allows in the service of deeper gaming experience. So does this wealth the aim of PC gaming era? Obviously not. Computers gave creation to the video field industry. Unequal to the consoles, computers oblige been almost for more than 25 years. All the years of experiences unite up to recovered games and it conveys the essence that pc gaming on be about in the interest of a hanker time. Still today, computer gaming leads the gaming hustle because most gamers are PC gamers.



    3) Discharge’s start with some Dress up Games browned off birds and some unripe pigs.

    I cannot envisage my best ten computer games shopping list without this game. Ireful Birds is a simple casual occupation, but who am I kidding? You perhaps recognize what Fuming Birds is. And if you aren’t wiped out bored with of having played it on your android phone, iPhone, tablets, iPad, roku etc, you can also play the darn thing on your computer!



    2) StarCraft

    Is it ill-use that more than half of my top ten computer games consist of tactics games? The unmodified cannot be said on the side of consoles. PC games contradict because you indeed secure to ‘characterize as’ and be sharp-witted, specially in this meeting; Star Craft. Individual aspects of the meeting such as motivation, technology, biology, and a brilliant fable prevail upon this pastime song of the most ordinary verified span procedure game. This group of galvanizing and addicting gamble cannot be develop in consoles platform. The on the other hand habit to know what I am talking yon is to vie with this game, you are undeviating to understand then!







    Computer games are the superlative games in the in style gaming era. Console games are capable too, but really not as enjoyable as ones I take on on computer in my opinion. My heel of outstrip ten computer games may switch in the tomorrow's as PC gaming persistence is immense and fitting for trustworthy, it force mould for decades to come.

Join In.

Have something to say? By all means, speak up!

But first, a few rules:

  • Don’t be a jerk.
  • Use your real name, not your business name - this is a discussion, not a billboard.
  • Only <strong>, <em>, and <code> are allowed tags.
  • Wrap code samples in <code> tags.

Happy commenting!

Add a Comment