views:

127

answers:

6

How to implode foreach() with comma?

foreach($names as $name) {
    //do something
    echo '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
}

Want to add comma after each link, except the last one.

+2  A: 
foreach($names as $name) {
    //do something
    $str .= '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>,';
}
echo substr($str,0,-1);

EDIT: as the comments point out, this way of doing things is a little error prone if you change the separator (precisely its length) and forget the substr parameter. So use the foreach method unless performance is absolutely critical.

Raveren
any other solution?
Happy
why, this is faster than implode.
Raveren
… until you change the separator and forget to adjust the `substr`.
janmoesen
@janmoesen that's very valid. Not a reason to give me a minus though :/
Raveren
Sorry, I was not giving you specifically a minus; just this approach. I've seen it too many times: building a query this way and then `substr`ing it into syntax errordom. I am trying to discourage this approach when there is a cleaner way with `implode`.
janmoesen
That's a reasonable point, I've edited the answer.
Raveren
-1 for militant microoptimization
Reinis I.
@reinis, wtf are you even talking about? `>So use the foreach method unless performance is absolutely critical.` Please recall the downvote.
Raveren
+1  A: 

Here is an ugly solution using echo:

 $total = (count($names) - 1 );

 foreach($names as $i => $name) 
 {
      if($i != $total) 
           echo '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>, ';
      else
           echo '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
 }
jakenoble
I would +1, except that counting the array on every iteration is quite wasteful (especially if the array is sizable)... Perhaps pre-compute (`$numOfElements = count($names) - 1; foreach ...`)...
ircmaxell
Good point - amended
jakenoble
+11  A: 

Raveren's solution is efficient and beautiful, but here is another solution too (which can be useful in similar scenarios):

$elements = array();
foreach($names as $name) {
    //do something
    $elements[] = '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
}
echo implode(',', $elements);
Emil Vikström
an array.. I like it!
Happy
In general, this one should be faster than concatenating strings. This would probably be even faster if the string was built using either string formatting or variable substitution: `$elements[] = "<a href=\"$url\" title=`...
Blixt
This one's slower though and is neither more readable nor functional. (not that it's worse, I'm just defending the micro-optimization)
Raveren
@Blixt, that's just a guess, I can't find the source, but concat and substr was benchmarked to be faster in all cases.
Raveren
True, it's just a guess, but generally, string formatting is more elegant too, so I wouldn't consider it a bad change even if it was slower. Also, an array of strings that is concatenated is, to me, more readable than stripping off the last character.
Blixt
+2  A: 
$first = TRUE;
foreach($names as $name) {
    //do something
    if(!$first) { echo ', '; }
    $first = FALSE;
    echo '<a href="', $url, '" title="', $title, '">', $name, '</a>';
}
knittl
Micro-optimisation: with echo, use commas instead of string concatenation: http://www.electrictoolbox.com/php-echo-commas-vs-concatenation/
Douglas
@douglas: good point :)
knittl
+9  A: 

You need to transform your array instead of iterating using foreach. You can do this with array_map.

PHP 5.3 syntax with closures

echo implode(", ", array_map(function($name) use($url, $title)
{
    return '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
}, $names));

Compatible syntaxe before PHP 5.3

function createLinkFromName($name)
{
    return '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
}
echo implode(", ", array_map('createLinkFromName', $names));

PHP 5.3 syntax with a better reability

function a_map($array, $function)
{
    return array_map($function, $array);
}

echo implode(", ", a_map($names, function($name) use($url, $title)
{
    return '<a href="' . $url . '" title="' . $title . '">' . $name .'</a>';
}));
Vincent Robert
very nice and elegant!
knittl
That's what I wanted to suggest... Nice usage of anonymous functions.
Stefan Gehrig
Frankly this borders on unreadable - especially with such a trivial task. Also it's most probably the slowest solution if you're into performance.
Raveren
I wanted to propose the same, but you have to be careful with `$url` and `$title`. They won't be set (at least in the before PHP 5.3 example).
Felix Kling
Isn't this far more complicated than necessary?
TRiG
It is not that complicated, unreadability comes from the array_map syntax that puts the function first which is not a wise decision when working with closures. I suggest defining a new function with the opposite order of arguments to improve readbility.
Vincent Robert
+1  A: 
$s = '';
foreach ($names as $name) {
  if ($s) $s .= ', ';
  $s .= '<a href="' . $url . '" title="' . $title . '">' . $name . '</a>';
}
TRiG
i think you meant `if(!$s)`, or better `if(strlen($s) == 0) $s = ', ';`
knittl
knittl, you can save a function call by checking isset($s[0]) instead of strlen \o/
Emil Vikström
No. `if ($s)` is correct. We don't want to start with a comma. We want to add one only from the second link on.
TRiG
But you managed to confuse me enough to edit my answer and then revert it.
TRiG
@TRiG: oh ok, i didn't realize you were using $s for both the delimiter and the string itself. sorry for the caused confusion
knittl