Implode an array with ", " and add "and " before the last item - php

This array holds a list of items, and I want to turn it into a string, but I don't know how to make the last item have a &/and before it instead of a comma.
1 => coke 2=> sprite 3=> fanta
should become
coke, sprite and fanta
This is the regular implode function:
$listString = implode(', ', $listArrau);
What's an easy way to do it?

A long-liner that works with any number of items:
echo join(' and ', array_filter(array_merge(array(join(', ', array_slice($array, 0, -1))), array_slice($array, -1)), 'strlen'));
Or, if you really prefer the verboseness:
$last = array_slice($array, -1);
$first = join(', ', array_slice($array, 0, -1));
$both = array_filter(array_merge(array($first), $last), 'strlen');
echo join(' and ', $both);
The point is that this slicing, merging, filtering and joining handles all cases, including 0, 1 and 2 items, correctly without extra if..else statements. And it happens to be collapsible into a one-liner.

I'm not sure that a one liner is the most elegant solution to this problem.
I wrote this a while ago and drop it in as required:
/**
* Join a string with a natural language conjunction at the end.
* https://gist.github.com/angry-dan/e01b8712d6538510dd9c
*/
function natural_language_join(array $list, $conjunction = 'and') {
$last = array_pop($list);
if ($list) {
return implode(', ', $list) . ' ' . $conjunction . ' ' . $last;
}
return $last;
}
You don't have to use "and" as your join string, it's efficient and works with anything from 0 to an unlimited number of items:
// null
var_dump(natural_language_join(array()));
// string 'one'
var_dump(natural_language_join(array('one')));
// string 'one and two'
var_dump(natural_language_join(array('one', 'two')));
// string 'one, two and three'
var_dump(natural_language_join(array('one', 'two', 'three')));
// string 'one, two, three or four'
var_dump(natural_language_join(array('one', 'two', 'three', 'four'), 'or'));
It's easy to modify to include an Oxford comma if you want:
function natural_language_join( array $list, $conjunction = 'and' ) : string {
$oxford_separator = count( $list ) == 2 ? ' ' : ', ';
$last = array_pop( $list );
if ( $list ) {
return implode( ', ', $list ) . $oxford_separator . $conjunction . ' ' . $last;
}
return $last;
}

You can pop last item and then join it with the text:
$yourArray = ('a', 'b', 'c');
$lastItem = array_pop($yourArray); // c
$text = implode(', ', $yourArray); // a, b
$text .= ' and '.$lastItem; // a, b and c

Try this:
$str = array_pop($array);
if ($array)
$str = implode(', ', $array)." and ".$str;

Another possible short solution:
$values = array('coke', 'sprite', 'fanta');
$values[] = implode(' and ', array_splice($values, -2));
print implode(', ', $values); // "coke, sprite and fanta"
It works fine with any number of values.

My go-to, similar to Enrique's answer, but optionally handles the oxford comma.
public static function listifyArray($array,$conjunction='and',$oxford=true) {
$last = array_pop($array);
$remaining = count($array);
return ($remaining ?
implode(', ',$array) . (($oxford && $remaining > 1) ? ',' : '') . " $conjunction "
: '') . $last;
}

I know im way to late for the answer, but surely this is a better way of doing it?
$list = array('breakfast', 'lunch', 'dinner');
$list[count($list)-1] = "and " . $list[count($list)-1];
echo implode(', ', $list);

This can be done with array_fill and array_map. It is also a one-liner (seems that many enjoy them)), but formated for readability:
$string = implode(array_map(
function ($item, $glue) { return $item . $glue; },
$array,
array_slice(array_fill(0, count($array), ', ') + ['last' => ' and '], 2)
));
Not the most optimal solution, but nevertheless.
Here is the demo.

try this
$arr = Array("coke","sprite","fanta");
$str = "";
$lenArr = sizeof($arr);
for($i=0; $i<$lenArr; $i++)
{
if($i==0)
$str .= $arr[$i];
else if($i==($lenArr-1))
$str .= " and ".$arr[$i];
else
$str .= " , ".$arr[$i];
}
print_r($str);

Try this,
<?php
$listArray = array("coke","sprite","fanta");
foreach($listArray as $key => $value)
{
if(count($listArray)-1 == $key)
echo "and " . $value;
else if(count($listArray)-2 == $key)
echo $value . " ";
else
echo $value . ", ";
}
?>

I just coded this based on the suggestions on this page. I left in my pseudo-code in the comments in case anyone needed it. My code differs from others here as it handles different-sized arrays differently and uses the Oxford comma notation for lists of three or more.
/**
* Create a comma separated list of items using the Oxford comma notation. A
* single item returns just that item. 2 array elements returns the items
* separated by "and". 3 or more items return the comma separated list.
*
* #param array $items Array of strings to list
* #return string List of items joined by comma using Oxford comma notation
*/
function _createOxfordCommaList($items) {
if (count($items) == 1) {
// return the single name
return array_pop($items);
}
elseif (count($items) == 2) {
// return array joined with "and"
return implode(" and ", $items);
}
else {
// pull of the last item
$last = array_pop($items);
// join remaining list with commas
$list = implode(", ", $items);
// add the last item back using ", and"
$list .= ", and " . $last;
return $list;
}
}

This is quite old at this point, but I figured it can't hurt to add my solution to the pile. It's a bit more code than other solutions, but I'm okay with that.
I wanted something with a bit of flexibility, so I created a utility method that allows for setting what the final separator should be (so you could use an ampersand, for instance) and whether or not to use an Oxford comma. It also properly handles lists with 0, 1, and 2 items (something quite a few of the answers here do not do)
$androidVersions = ['Donut', 'Eclair', 'Froyo', 'Gingerbread', 'Honeycomb', 'Ice Cream Sandwich', 'Jellybean', 'Kit Kat', 'Lollipop', 'Marshmallow'];
echo joinListWithFinalSeparator(array_slice($androidVersions, 0, 1)); // Donut
echo joinListWithFinalSeparator(array_slice($androidVersions, 0, 2)); // Donut and Eclair
echo joinListWithFinalSeparator($androidVersions); // Donut, Eclair, Froyo, Gingerbread, Honeycomb, Ice Cream Sandwich, Jellybean, Kit Kat, Lollipop, and Marshmallow
echo joinListWithFinalSeparator($androidVersions, '&', false); // Donut, Eclair, Froyo, Gingerbread, Honeycomb, Ice Cream Sandwich, Jellybean, Kit Kat, Lollipop & Marshmallow
function joinListWithFinalSeparator(array $arr, $lastSeparator = 'and', $oxfordComma = true) {
if (count($arr) > 1) {
return sprintf(
'%s%s %s %s',
implode(', ', array_slice($arr, 0, -1)),
$oxfordComma && count($arr) > 2 ? ',':'',
$lastSeparator ?: '',
array_pop($arr)
);
}
// not a fan of this, but it's the simplest way to return a string from an array of 0-1 items without warnings
return implode('', $arr);
}

OK, so this is getting pretty old, but I have to say I reckon most of the answers are very inefficient with multiple implodes or array merges and stuff like that, all far more complex than necessary IMO.
Why not just:
implode(',', array_slice($array, 0, -1)) . ' and ' . array_slice($array, -1)[0]

Simple human_implode using regex.
function human_implode($glue = ",", $last = "y", $elements = array(), $filter = null){
if ($filter) {
$elements = array_map($filter, $elements);
}
$str = implode("{$glue} ", $elements);
if (count($elements) == 2) {
return str_replace("{$glue} ", " {$last} ", $str);
}
return preg_replace("/[{$glue}](?!.*[{$glue}])/", " {$last}", $str);
}
print_r(human_implode(",", "and", ["Joe","Hugh", "Jack"])); // => Joe, Hugh and Jack

Another, although slightly more verbose, solution I came up with. In my situation, I wanted to make the words in the array plural, so this will add an "s" to the end of each item (unless the word already ends in 's':
$models = array("F150","Express","CR-V","Rav4","Silverado");
foreach($models as $k=>$model){
echo $model;
if(!preg_match("/s|S$/",$model))
echo 's'; // add S to end (if it doesn't already end in S)
if(isset($models[$k+1])) { // if there is another after this one.
echo ", ";
if(!isset($models[$k+2]))
echo "and "; // If this is next-to-last, add ", and"
}
}
}
outputs:
F150s, Express, CR-Vs, Rav4s, and Silverados

It's faster then deceze's solution and works with huge arrays (1M+ elements). The only flaw of both solutions is a poor interaction with a number 0 in a less then three elements arrays becouse of array_filter use.
echo implode(' and ', array_filter(array_reverse(array_merge(array(array_pop($array)), array(implode(', ',$array))))));

Related

How remove the last comma in my string?

I've been trying to figgure out using substr, rtrim and it keeps removing all the commas. And if it doesn't nothing shows up. So I am basicly stuck and require some help.. Would've been apriciated.
if(is_array($ids)) {
foreach($ids as $id) {
$values = explode(" ", $id);
foreach($values as $value) {
$value .= ', ';
echo ltrim($value, ', ') . '<br>';
}
}
}
I am guessing that you are trying to take an array of strings of space separated ids and flatten it into a comma separated list of the ids.
If that is correct you can do it as:
$arr = [
'abc def ghi',
'jklm nopq rstu',
'vwxy',
];
$list = implode(', ', explode(' ', implode(' ', $arr)));
echo $list;
output:
abc, def, ghi, jklm, nopq, rstu, vwxy
Change ltrim by rtrim:
ltrim — Strip whitespace (or other characters) from the beginning of a string
rtrim — Strip whitespace (or other characters) from the end of a string
<?php
$ids = Array ( 1,2,3,4 );
$final = '';
if(is_array($ids)) {
foreach($ids as $id) {
$values = explode(" ", $id);
foreach($values as $value) {
$final .= $value . ', ';
}
}
echo rtrim($final, ', ') . '<br>';
echo substr($final, 0, -2) . '<br>'; //other solution
}
?>
If your array looks like;
[0] => 1,
[1] => 2,
[2] => 3,
...
The following should suffice (not the most optimal solution);
$string = ''; // Create a variable to store our future string.
$iterator = 0; // We will need to keep track of the current array item we are on.
if ( is_array( $ids ) )
{
$array_length = count( $ids ); // Store the value of the arrays length
foreach ( $ids as $id ) // Loop through the array
{
$string .= $id; // Concat the current item with our string
if ( $iterator >= $array_length ) // If our iterator variable is equal to or larger than the max value of our array then break the loop.
break;
$string .= ", "; // Append the comma after our current item.
$iterator++; // Increment our iterator variable
}
}
echo $string; // Outputs "1, 2, 3..."
Use trim() function.
Well, if you have a string like this
$str="foo, bar, foobar,";
use this code to remove the Last comma
<?Php
$str="foo, bar, foobar,";
$string = trim($str, " ,");
echo $string;
output: foo, bar, foobar

Changing output text from array

I'm currently printing table column names, filtered, but need to change the text for each.
For instance, the following:
$row = mysql_fetch_assoc($query_columns);
echo implode(', ', array_keys( array_filter( $row )));
outputs:
column_one, column_two, column_five
What I haven't been able to achieve, without a lengthy if statement, is a way to have complete control over the output, such as changing each...
Expected output:
This is column one, and Column two, but this is column five
Any help is much appreciated!
Table structure:
Category
--------
user_id
live_out
live_in
short_term
weekday
weekend
unsure
Current output (based on user selection):
live_out, live_in, weekday
Desired output:
Yes as live-out, Yes as live-in, Yes for regular weekdays
You have echo implode(', ', array_keys( array_filter( $row )));
The implode() uses the array to concatenate the keys to one string.
One solution would be to use string concatenation, when iterating the array:
$array = array_keys( array_filter( $row ))); // gets you the keys array
$string = 'This is ';
foreach($array as $item)
{
$string .= $item . ', '; // append item to string with comma added
}
$string .= '. That's all.';
echo $string;
If you want custom text per item, you need to detect the item when iterating.
You might use if and strpos() for that, like so:
$string = 'This is ';
foreach($array as $item)
{
$string .= $item . ', '; // append item to string with comma added
if(false !== strpos($item, 'Column One')) { // look for Column One in $item
$string .= ' (found column one)';
}
}
$string .= ' That's all.';
echo $string;
Replace:
$string = str_replace('Column One', 'Column XXX', $string);
works also for multiple items at once
$replace = array('Column One', 'Column Two');
$with = array('Column XX1', 'Column XX2');
$string = str_replace($replace, $with, $string);
Take care about $string = and $string .=.
The first assigns the value, the second appends the value.
Based on your table structure
$string = '';
foreach($array as $item)
{
if(false !== strpos($item, 'live_out')) {
$string .= 'Yes for '. $item . ', ';
}
if(false !== strpos($item, 'live_in')) {
$string .= 'Yes for '. $item . ', ';
}
if(false !== strpos($item, 'weekdays')) {
$string .= 'Yes for regular '. $item;
}
}
echo $string;
You can change each item in the array like this;
$column_names[0] = "Add this to the start of column one ". $column_names[0] . ", and this to the end of column one.";
0 is the first item in the array, and 1 is the second and so on.
Remember you need to do this to the array you get from array_keys in this case.
Otherwise you could make a function like this;
function addStrToArrayElements($array, $str_start, $str_end, $array_keys=NULL){
foreach($array as $key => $item){
if(array_key_exists($key, $array_keys) || is_null($array_keys) ){
$ret[$key] = $str_start.$item.$str_end;
}
}
return $ret;
}
$array is the array you want the elements to change on, $str_start is the string to put in front of the item, $str_end the on behind the item, and $array_keys is an array of the keys of which elements you want changed, if you leave it blank, it will do the same to all elements of $array.
For the example you have above you would do this:
$column_names = array_keys($row);
addStrToArrayElements($column_names, 'This is ', ',', array(0));
addStrToArrayElements($column_names, ' and ', ',', array(1));
addStrToArrayElements($column_names, ' but this is ', '', array(2));
$output = implode('', $column_names);
echo str_replace('column_', 'column ', $output);
That should output
This is column one, and column two, but this is column five
Say you have the following array called $array1 below:
<?php
$array1 = array("hey","its","me","so","what");
$newarray = array();
$i=0;
foreach ($array1 as $value)
{
$newarray[] = "<span id=" . "color$i" . ">" . $value . "</span>";
$i++;
}
echo implode(",",$newarray);
?>
You can use the algorithm to set different styles for all the columns using the spans created. So for instance you can set a style in the head or external stylesheet like so:
<style>
#color0{ color:red;}
#color1{ color:blue;}
#color2{ color:yellow;}
</style>
I tested this out and it seems to work just fine!

Add commas to items and with "and" near the end in PHP [duplicate]

This question already has answers here:
Implode an array with ", " and add "and " before the last item
(16 answers)
Closed 10 months ago.
I want to add a comma to every item except the last one. The last one must have "and".
Item 1, Item 2 and Item 3
But items can be from 1 +
So if one item:
Item 1
If two items:
Item 1 and Item 2
If three items:
Item 1, Item 2 and Item 3
If four items:
Item 1, Item 2, Item 3 and Item 4
etc, etc.
Here’s a function for that; just pass the array.
function make_list($items) {
$count = count($items);
if ($count === 0) {
return '';
}
if ($count === 1) {
return $items[0];
}
return implode(', ', array_slice($items, 0, -1)) . ' and ' . end($items);
}
Demo
minitech's solution on the outset is elegant, except for one small issue, his output will result in:
var_dump(makeList(array('a', 'b', 'c'))); //Outputs a, b and c
But the proper formatting of this list (up for debate) should be; a, b, and c. With his implementation the next to last attribute will never have ',' appended to it, because the array slice is treating it as the last element of the array, when it is passed to implode().
Here is an implementation I had, and properly (again, up for debate) formats the list:
class Array_Package
{
public static function toList(array $array, $conjunction = null)
{
if (is_null($conjunction)) {
return implode(', ', $array);
}
$arrayCount = count($array);
switch ($arrayCount) {
case 1:
return $array[0];
break;
case 2:
return $array[0] . ' ' . $conjunction . ' ' . $array[1];
}
// 0-index array, so minus one from count to access the
// last element of the array directly, and prepend with
// conjunction
$array[($arrayCount - 1)] = $conjunction . ' ' . end($array);
// Now we can let implode naturally wrap elements with ','
// Space is important after the comma, so the list isn't scrunched up
return implode(', ', $array);
}
}
// You can make the following calls
// Minitech's function
var_dump(makeList(array('a', 'b', 'c')));
// string(10) "a, b and c"
var_dump(Array_Package::toList(array('a', 'b', 'c')));
// string(7) "a, b, c"
var_dump(Array_Package::toList(array('a', 'b', 'c'), 'and'));
string(11) "a, b, and c"
var_dump(Array_Package::toList(array('a', 'b', 'c'), 'or'));
string(10) "a, b, or c"
Nothing against the other solution, just wanted to raise this point.
Here's a variant that has an option to support the controversial Oxford Comma and takes a parameter for the conjunction (and/or). Note the extra check for two items; not even Oxford supporters use a comma in this case.
function conjoinList($items, $conjunction='and', $oxford=false) {
$count = count($items);
if ($count === 0){
return '';
} elseif ($count === 1){
return $items[0];
} elseif ($oxford && ($count === 2)){
$oxford = false;
}
return implode(', ', array_slice($items, 0, -1)) . ($oxford? ', ': ' ') . $conjunction . ' ' . end($items);
}
You can implode the X - 1 items with a comma and add the last one with "and".
You can do it like this:
$items = array("Item 1", "Item 2", "Item 3", "Item 4");
$item = glueItems($items);
function glueItems($items) {
if (count($items) == 1) {
$item = implode(", ", $items);
} elseif (count($items) > 1) {
$last_item = array_pop($items);
$item = implode(", ", $items) . ' and ' . $last_item;
} else {
$item = '';
}
return $item;
}
echo $item;
My function, based on others here, that I feel like is more streamlined. Also always adds in the Oxford comma because there really is no "controversy" or debate about whether it's correct or not to use.
//feed in an array of words to turn it into a written list.
//['bacon'] turn into just 'bacon'
//['bacon','eggs'] turns into 'bacon and eggs'
//['bacon','eggs','ham'] turns into 'bacon, eggs, and ham'
function writtenList($items) {
//nothing, or not an array? be done with this
if (!$items || !is_array($items)) return '';
//list only one or two items long, this implosion should work fine
if (count($items)<=2) {
return implode(' and ',$items);
//else take off the last item, implode the rest with just a comma and the remaining item
} else {
$last_item = array_pop($items);
return implode(", ",$items).', and '.$last_item;
}
}
Well if it is an array just use implode(...)
Example:
$items = array("Item 1", "Item 2", "Item 3", "Item 4");
$items[count($items) - 1] = "and " . $items[count($items) - 1];
$items_string = implode(", ", $items);
echo $items_string;
Demo: http://codepad.viper-7.com/k7xISG

Generating a PHP list with "and"

What's an efficient way to format an array in PHP as a list with commas and the word "and" before the last element?
$array = Array('a','b','c','d');
I want to produce the string "a, b, c, and d"
I would modify the $array in place to add the "and", and then join the parts with commas for output:
array_push($array, " and " . array_pop($array));
print join(", ", $array);
But you might as well just use array_pop to separate the last entry, join the rest, and then append the "and" and last entry.
I suppose that should do it:
$array[ sizeof($array) - 1 ] = 'and ' . $array[ sizeof($array) - 1 ];
$list = implode(', ', $array);
There are a number of ways you could do it, here is one:
<?
$array = Array('a','b','c','d');
$string=implode(',',$array);
$nstring=substr_replace($string,' and ', -1, 0);
echo $nstring;
<?php
$array = Array('a','b','c','d');
$array[count($array)-1] = 'and '.$array[count($array)-1];
echo implode($array, ', ');
?>
http://codepad.org/7oeni6xv
Try:
function arrayEnum($array)
{
$string = '';
for (i = 0, $l = count($array); i < $l; ++i)
{
$string .= $array[$i];
if (i != ($l -1)) $string .= ', ';
if (i == ($l -2)) $string .= 'and '
}
}
$array = Array('a','b','c','d');
echo $array; // a, b, c, and d
$array = Array('one','two','three','four');
echo $array; // one, two, three and four
That function will work with any length of data.

"Unfolding" a String

I have a set of strings, each string has a variable number of segments separated by pipes (|), e.g.:
$string = 'abc|b|ac';
Each segment with more than one char should be expanded into all the possible one char combinations, for 3 segments the following "algorithm" works wonderfully:
$result = array();
$string = explode('|', 'abc|b|ac');
foreach (str_split($string[0]) as $i)
{
foreach (str_split($string[1]) as $j)
{
foreach (str_split($string[2]) as $k)
{
$result[] = implode('|', array($i, $j, $k)); // more...
}
}
}
print_r($result);
Output:
$result = array('a|b|a', 'a|b|c', 'b|b|a', 'b|b|c', 'c|b|a', 'c|b|c');
Obviously, for more than 3 segments the code starts to get extremely messy, since I need to add (and check) more and more inner loops. I tried coming up with a dynamic solution but I can't figure out how to generate the correct combination for all the segments (individually and as a whole). I also looked at some combinatorics source code but I'm unable to combine the different combinations of my segments.
I appreciate if anyone can point me in the right direction.
Recursion to the rescue (you might need to tweak a bit to cover edge cases, but it works):
function explodinator($str) {
$segments = explode('|', $str);
$pieces = array_map('str_split', $segments);
return e_helper($pieces);
}
function e_helper($pieces) {
if (count($pieces) == 1)
return $pieces[0];
$first = array_shift($pieces);
$subs = e_helper($pieces);
foreach($first as $char) {
foreach ($subs as $sub) {
$result[] = $char . '|' . $sub;
}
}
return $result;
}
print_r(explodinator('abc|b|ac'));
Outputs:
Array
(
[0] => a|b|a
[1] => a|b|c
[2] => b|b|a
[3] => b|b|c
[4] => c|b|a
[5] => c|b|c
)
As seen on ideone.
This looks like a job for recursive programming! :P
I first looked at this and thought it was going to be a on-liner (and probably is in perl).
There are other non-recursive ways (enumerate all combinations of indexes into segments then loop through, for example) but I think this is more interesting, and probably 'better'.
$str = explode('|', 'abc|b|ac');
$strlen = count( $str );
$results = array();
function splitAndForeach( $bchar , $oldindex, $tempthread) {
global $strlen, $str, $results;
$temp = $tempthread;
$newindex = $oldindex + 1;
if ( $bchar != '') { array_push($temp, $bchar ); }
if ( $newindex <= $strlen ){
print "starting foreach loop on string '".$str[$newindex-1]."' \n";
foreach(str_split( $str[$newindex - 1] ) as $c) {
print "Going into next depth ($newindex) of recursion on char $c \n";
splitAndForeach( $c , $newindex, $temp);
}
} else {
$found = implode('|', $temp);
print "Array length (max recursion depth) reached, result: $found \n";
array_push( $results, $found );
$temp = $tempthread;
$index = 0;
print "***************** Reset index to 0 *****************\n\n";
}
}
splitAndForeach('', 0, array() );
print "your results: \n";
print_r($results);
You could have two arrays: the alternatives and a current counter.
$alternatives = array(array('a', 'b', 'c'), array('b'), array('a', 'c'));
$counter = array(0, 0, 0);
Then, in a loop, you increment the "last digit" of the counter, and if that is equal to the number of alternatives for that position, you reset that "digit" to zero and increment the "digit" left to it. This works just like counting with decimal numbers.
The string for each step is built by concatenating the $alternatives[$i][$counter[$i]] for each digit.
You are finished when the "first digit" becomes as large as the number of alternatives for that digit.
Example: for the above variables, the counter would get the following values in the steps:
0,0,0
0,0,1
1,0,0 (overflow in the last two digit)
1,0,1
2,0,0 (overflow in the last two digits)
2,0,1
3,0,0 (finished, since the first "digit" has only 3 alternatives)

Categories