Format A List Of Items In PHP

It is usual when writing a list of items to separate each item with a comma, except the last two items, which are separated with the word "and". I recently needed to implement a function that took a string and converted it into a list of this type so I thought I would expand on it and post it here.

The function takes a single parameter, which can either be an array or a comma separated string. If an array is passed to the function then it is converted into a comma separated string and then passed onto the next part in the function. The function then removes any trailing commas, any commas that have nothing in between them and then makes sure that each comma has a single space after it. The final step is to replace the last comma with the word "and". Once the manipulation is complete then the resulting string is returned. If the string (after removing any trailing commas) doesn't contain any commas then it is simply returned.

Here is the function in full, will comments for each step.

/**
 * This function will take a string in the format of a single item or
 * multiple items in the format 1,2,3,4,5 or an array of items.
 * The output will be a readable set of items with the last two items 
 * separated by " and ".
 *
 * @param  string|array $numbers The list of items as a string or array.
 * @return string                The formatted items.
 */
function formatItems($numbers)
{
    if (is_array($numbers)) {
        // If numbers is an array then implode it into a comma separated string.
        $numbers = implode(',', $numbers);
    }
 
    if (is_string($numbers)) {
        // Make sure all commas have a single space character after them.
        $numbers = preg_replace("/(\s*?,\s*)/", ", ", $numbers);
        // Remove any spare commas
        $numbers = preg_replace("/(,\s)+/", ", ", $numbers);        
        // The string contains commas, find the last comma in the string.
        $lastCommaPos = strrpos($numbers, ',') - strlen($numbers);
        // Replace the last ocurrance of a comma with " and "
        $numbers = substr($numbers, 0, $lastCommaPos) . str_replace(',', ' and', substr($numbers, $lastCommaPos));
    }
    return $numbers;
}

Here are a few examples of this function in action.

echo formatItems('1');
echo formatItems('1,2');
echo formatItems('1,2,3,4,5,6,7');
echo formatItems(range(1,6));
echo formatItems(range('a','g'));
echo formatItems('sdfgdf g,sdf, g,dfg,df,g ,df g,df,g ,d fg');
echo formatItems('1.45/76,5/,85/6.,45./6,456');
echo formatItems('sdfah,      ,,, ,, ,,,, ,  ,  ,  568776ythU~O@)_}_:{>9l,65653224,253,4,236,56,98./,978/59');
echo formatItems('4575,8 56,456,36,45656      ,,    , 4, 56, 546, 546, , 6, 456, , ');

This code produces the following output.

1
1 and 2
1, 2, 3, 4, 5, 6 and 7
1, 2, 3, 4, 5 and 6
a, b, c, d, e, f and g
sdfgdf g, sdf, g, dfg, df, g, df g, df, g and d fg
1.45/76, 5/, 85/6., 45./6 and 456
sdfah, 568776ythU~O@)_}_:{>9l, 65653224, 253, 4, 236, 56, 98./ and 978/59
4575, 8 56, 456, 36, 45656, 4, 56, 546, 546, 6 and 456

UPDATE: 08/05/2010

Thanks to Carl for finding a little bug with regards to how commas in messy strings are looked at within the function. I had a quick look at code I wrote to clean up a list of user inputted items and included it in this code. Here is the new version of the function.

/**
 * This function will take a string in the format of a single item or
 * multiple items in the format 1,2,3,4,5 or an array of items.
 * The output will be a readable set of items with the last two items 
 * separated by " and ".
 *
 * @param  string|array $numbers The list of items as a string or array.
 * @return string                The formatted items.
 */
function formatItems($numbers)
{
    if (is_array($numbers)) {
        // If numbers is an array then implode it into a comma separated string.
        $numbers = implode(',', $numbers);
    }
 
    if (is_string($numbers)) {
        /* 
	    Make sure all commas have a single space character after them and that	
	    there are no double commas in the string.
	    */	
	    $numbers = trim($numbers);
	    $patterns[0] = '/\s*,\s*/';
	    $patterns[1] = '/,{2,}/';
	    $patterns[2] = '/,+$/';
	    $patterns[3] = '/^,+/';
	    $patterns[4] = '/,/';
	    $replacements[0] = ',';
	    $replacements[1] = ',';
	    $replacements[2] = '';
	    $replacements[3] = '';
	    $replacements[4] = ', ';
	    $numbers= preg_replace($patterns, $replacements, $numbers);

        // The string contains commas, find the last comma in the string.
        $lastCommaPos = strrpos($numbers, ',') - strlen($numbers);

        // Replace the last ocurrance of a comma with " and "
        $numbers = substr($numbers, 0, $lastCommaPos) . str_replace(',', ' and ', substr($numbers, $lastCommaPos));
    }
    return $numbers;
}

Comments

Hi, Thanks for putting together these tutorials. They're a great combination of educational value and practicality as well. I've spent much of Saturday afternoon and early evening looking through them but my wife needs a bit of attention. I couldn't get the last array to work (4575, 8 56, 456, 36, 45656, 4, 56, 546, 546, 6 and 456), instead I got; 4575, 8 56, 456, 36, 45656, 4, 56, 546, 546, 6, 456 and I managed to fudge it by putting in an extra check; // remove any simulutaneous , space sequence $numbers = preg_replace("/,\s,\s/", "", $numbers); It's not very pretty but it works, and I guess I've got some way to go! Thanks again.
Permalink
Hi Carl, I'm glad you find this site useful! I have put a lot of work into this site so it's nice to get some positive feedback :) Thanks for telling me about the problem you found with the function, I had a play around with your suggestion and it worked very well. I remembered writing some code that cleaned up a comma separated list so I dug that out and replaced the comma filtering in this function. Rather than delete and replace the original function I just added an update to the bottom. Feel free to add any more comments if you find any more problems. Phil
Name
Philip Norton
Permalink
first, thank you so much for posting this. i'm quite new to php and would not have been able to do this on my own. and, of course, i learned a great deal just from reading the code. i do have one very noobish question: how do i call the function and store the comma separated list in a variable (as text)? i thought something like:$list = formatItems($assignees);might work but i'm not having any luck. the $assignees variable is a text array. thanks!!
Permalink
That would work. The $list variable will contain your formatted list of items. :)
Name
Philip Norton
Permalink
For some reason, when I do it that way, it doesn't recognize that I'm calling a function -- even though I placed it in the file with my other functions that ARE recognized.
Permalink
It worked for me when I tried it just now, here's the code I used (with the function pasted in at the top).
$assignees = '1,,67 ,8,8,67,53,45,45, 34,5q34,dguhdfg ,sdfghdfgh,sdf s,df g,sdf ,,dfg ';

$list = formatItems($assignees);

echo $list;
Name
Philip Norton
Permalink
hmmmm... this is soooo kicking my butt. i'm pulling the array from a mysql database with:
foreach($_POST['uids'] as $userid) {
 $database->executeUpdate("INSERT INTO bookings (eid, uid) VALUES (?, ?)", $_POST['bookedeventid'], $userid);
 $emailaddress[] = $database->executeQueryRows("SELECT email FROM users WHERE uid =?", $userid);
}
executeUpdate and executeQueryRows are functions in the same file as the formatItems function, so i know the file is getting loaded. they replace the "?" with the values at the end of each statement (i.e. $_POST['bookedeventid] and $userid. does the fact that i'm pulling the array from a mysql table matter?
Permalink
nevermind, i figured out what i did wrong. the function works, though for some reason the value that is returned is still "Array".
Permalink
you were right and it works fine. the fault was mine. feel free to delete my posts.
Permalink
Well I'm happy you managed to solve your problem! It's all part of programming I suppose. I have lost count of the number of times I've sat in front of code going "why doesn't this work" only to find that I had been unbelievably stupid. :)
Name
Philip Norton
Permalink

Hi Phil,

Thanks heaps for this! I was wondering if you might be able to help me with something similar - I want to automatically create list items <li> when ever some enters a line break. So when the user enters

Item 1

Item2

Item3

The script changes it to:

<li>item 1</li>

<li>item 2</li>

<li>item 3</li>

 

Any idea of how I would go about this? Thanks heaps!

Permalink

Should be quite simple. All you need to do is replace line breaks with the string '&lt;/li&gt;&lt;li&gt;' and then wrap the whole thing in a list item to ensure the correct markup.

Name
Philip Norton
Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
2 + 4 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.