Reputation: 33996
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?
Upvotes: 93
Views: 58439
Reputation: 47991
I want to pursue a functional-style one-liner for this task with no filters, no new/temporary variables, and correctly return a string from an array of zero or more words/elements.
I use the variables $conjunction
to contain the single replacement word and
(wrapped with spaces) and $separator
to contain the delimiting string to be used between all elements except for the last element.
Implode $conjunction
, then implode $separator
:
function mickmackusa1(
array $words,
string $separator = ', ',
string $conjunction = ' and '
): string {
return implode(
$separator,
$words + ['last' => implode($conjunction, array_splice($words, -2))]
);
}
Implode $separator
, then implode $conjunction
:
function mickmackusa2(
array $words,
string $separator = ', ',
string $conjunction = ' and '
): string {
return implode(
$conjunction,
[
...(array) (implode($separator, array_splice($words, 0, -1)) ?: []),
...$words
]
);
}
I also wanted to check the success of all previous answers with arrays of 0 to 5 words. Here are my results: (Demo)
user \ elements | 0️⃣ | 1️⃣ | 2️⃣ | 3️⃣ | 4️⃣ | 5️⃣ |
---|---|---|---|---|---|---|
mickmackusa1 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
mickmackusa2 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
deceze | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
jJWright | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
sevavietl | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
vision | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
astha | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
podolinek | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
blazejKlisz | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
raviMisra | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
angryDan | 💩 | ✅ | ✅ | ✅ | ✅ | ✅ |
enrique | 💩 | ✅ | ✅ | ✅ | ✅ | ✅ |
planetX | ✅ | 💩 | ✅ | ✅ | ✅ | ✅ |
jercSi | 💩 | 💩 | ✅ | ✅ | ✅ | ✅ |
zkent | 💩 | ✅ | ✅ | 💩 | 💩 | 💩 |
uruapanmexicansong | ✅ | ✅ | ✅ | 💩 | 💩 | 💩 |
josh | ✅ | ✅ | 💩 | 💩 | 💩 | 💩 |
josh | ✅ | ✅ | 💩 | 💩 | 💩 | 💩 |
alexRussell | ✅ | 💩 | 💩 | 💩 | 💩 | 💩 |
greenButterfly Warning: Undefined array key 0 |
💩 | 💩 | ✅ | ✅ | ✅ | ✅ |
jackB Warning: Undefined array key -1 |
💩 | 💩 | 💩 | 💩 | 💩 | 💩 |
For anyone using Laravel, there is a dedicated collection method and an array helper which are built precisely for this task -- join()
. PHPize Demo
function laravelCollectJoin(array $words, string $separator = ', ', string $conjunction = ' and '): string {
return collect($words)->join($separator, $conjunction);
}
function laravelArrJoin(array $words, string $separator = ', ', string $conjunction = ' and '): string {
return Arr::join($words, $separator, $conjunction);
}
Test results:
func \ elements | 0️⃣ | 1️⃣ | 2️⃣ | 3️⃣ | 4️⃣ | 5️⃣ |
---|---|---|---|---|---|---|
laravelCollectJoin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
laravelArrJoin | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
Upvotes: 1
Reputation: 191
Simply use the function
function glue($array, $allButLast = ', ', $last = ' and ')
{
if (empty($array)) {
return '';
}
$glued = implode($allButLast, $array);
$pos = strrpos($glued, $allButLast);
if ($pos !== false) {
$glued = substr_replace($glued, $last, $pos, strlen($allButLast));
}
return $glued;
}
This is inspired from the answer by Mischa for the question
Upvotes: -2
Reputation: 176
Not the most elegant, but we all know, a programmer is for the most part reader and writer and has to count with limit states
function getStringFromList(
array $list,
string $itemsSeparator = ', ',
string $lastItemSeparator = 'and',
bool $removeDuplicates = false
): string {
// supported localization for "and" in basic form
$lastItemSeparator = ' ' . $lastItemSeparator . ' ';
// clean the array values
if ($removeDuplicates) {
$list = array_unique($list);
}
$list = array_values($list);
// treatment of the array with 0 or 1 cell
if (count($list) === 0) {
return "";
} elseif (count($list) === 1) {
return $list[0];
}
// FINALLY return string from list
$firstItems = implode($itemsSeparator, array_slice($list, 0, -1));
$lastItem = array_slice($list, -1)[0];
return $firstItems . $lastItemSeparator . $lastItem;
}
Upvotes: 0
Reputation: 3281
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;
}
Upvotes: 117
Reputation: 172
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
Upvotes: 0
Reputation: 3792
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.
Upvotes: 1
Reputation: 3388
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
Upvotes: 0
Reputation: 31
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;
}
Upvotes: 3
Reputation: 323
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]
Upvotes: 0
Reputation: 8159
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);
}
Upvotes: 0
Reputation: 522441
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.
Upvotes: 138
Reputation: 980
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;
}
}
Upvotes: 0
Reputation: 145438
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.
Upvotes: 4
Reputation: 219
Try this:
$str = array_pop($array);
if ($array)
$str = implode(', ', $array)." and ".$str;
Upvotes: 16
Reputation: 1730
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))))));
Upvotes: -2
Reputation: 549
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);
Upvotes: 1
Reputation: 1607
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 . ", ";
}
?>
Upvotes: 0
Reputation: 1734
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);
Upvotes: 0
Reputation: 1047
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
Upvotes: 32