3

I want to separate an array with comma and word.

This is the code that i make:

$array = array( 'user1', 'user2', 'user3', 'user4', 'user5' );
$last = array_pop( $array );
$string = count( $array ) ? implode( ", ", $array ) . " and " . $last : $last;

printf( 'Authors: %s', $string );

My code print this:

Authors: user1, user2, user3, user4 and user5

I successfully made a code to implode the array with a comma and search for the last key and separate it with a word 'and'.

But that's what I want but I failed after more than 2 days of work on this:

Authors: user1, user2, and user3 and 2 others.

John
  • 171
  • 10
  • Is your goal only printing it ? or you want to make new array ? – M4HdYaR Sep 05 '18 at 22:07
  • @M4HdYaR First thank you very much for trying to help me. Second with the same array that i used in my code. I want to print the first and second users and then add word 'and' before the third user and if more in the array add x others. – John Sep 05 '18 at 22:09
  • Related: https://stackoverflow.com/q/18476587/2943403 – mickmackusa Sep 05 '18 at 22:20
  • @mickmackusa Here's the array $array = array( 'user1', 'user2', 'user3', 'user4', 'user5' ); I want from this array print this: Authors: user1, user2, and user3 and 2 others. ( I want to print the first and second users and separate them with a comma and then add the word 'and' before the third user and if more in the array add x others. ). Are you understand now? – John Sep 05 '18 at 22:27
  • Will you always have at least 3 elements? Do we need to accommodate the possibility of `0` elements, `1` element, `2` elements as well? – mickmackusa Sep 05 '18 at 22:30
  • @mickmackusa No, I am not sure that the array of the user's list will always have at least 3 elements maybe more and maybe less. – John Sep 05 '18 at 22:32

4 Answers4

3

While I always look for the clever calculated process for handling a task like this, I'll offer a much more pedestrian alternative. (For the record, I hate writing a battery of if-elseif-else conditions almost as much as I hate the verbosity of a switch-case block.) For this narrow task, I am electing to abandon my favored array functions and conditionally print the values with their customized delimiters.

While this solution is probably least scalable on the page, it is likely to be the easiest to comprehend (relate the code to its generated output). If this is about "likes" or something, I don't really see a large demand for scalability -- but I could be wrong.

Pay close attention to the 2-count case. My solution delimits the elements with and instead of , which seems more English/human appropriate.

Code: (Demo)

$arrays[] = array();
$arrays[] = array('user1');
$arrays[] = array('user1', 'user2');
$arrays[] = array('user1', 'user2', 'user3');
$arrays[] = array('user1', 'user2', 'user3', 'user4');
$arrays[] = array('user1', 'user2', 'user3', 'user4', 'user5');

foreach ($arrays as $i => $array) {
    echo "TEST $i: ";
    if (!$count = sizeof($array)) {
        echo "nobody";
    } elseif ($count == 1) {
        echo $array[0];
    } elseif ($count == 2) {
        echo "{$array[0]} and {$array[1]}";
    } elseif ($count == 3) {
        echo "{$array[0]}, {$array[1]}, and {$array[2]}";
    } else {
        echo "{$array[0]}, {$array[1]}, {$array[2]}, and " , $count - 3, " other" , ($count != 4 ? 's' : '');
    }
    echo "\n---\n";
}

Output:

TEST 0: nobody
---
TEST 1: user1
---
TEST 2: user1 and user2
---
TEST 3: user1, user2, and user3
---
TEST 4: user1, user2, user3, and 1 other
---
TEST 5: user1, user2, user3, and 2 others
---

p.s. A more compact version of the same handling:

Code: (Demo)

if (!$count = sizeof($array)) {
    echo "nobody";
} elseif ($count < 3) {
    echo implode(' and ', $array);
} else{
    echo "{$array[0]}, {$array[1]}";
    if ($count == 3) {
        echo ", and {$array[2]}";
    } else {
        echo ", {$array[2]}, and " , $count - 3, " other" , ($count != 4 ? 's' : '');
    }
}

And finally, here's an approach that "doctors up" the array and prepends and to the final element. I think anyway you spin this task, there's going to be a degree of convolution.

Code: (Demo)

$count = sizeof($array);
if ($count < 3) {
    echo implode(' and ', $array);
} else {
    if ($count == 3) {
        $array[2] = "and {$array[2]}";
    } else {
        $more = $count - 3;
        array_splice($array, 3, $more, ["and $more other" . ($more > 1 ? 's' : '')]);
    }
    echo implode(', ', $array);
}

During a CodeReview, I realized a "pop-prepend-push" technique: https://codereview.stackexchange.com/a/255554/141885


From PHP8, match() is also available. An implementation might look like this: (Demo)

$count = count($array);
echo match ($count) {
         0 => 'nobody',
         1 => $array[0],
         2 => implode(' and ', $array),
         3 => "{$array[0]}, {$array[1]}, and {$array[2]}",
         default => "{$array[0]}, {$array[1]}, {$array[2]}, and " . $count - 3 . " other" . ($count != 4 ? 's' : '')
     };
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
2

Here's one way to do it:

Start by imploding the first two array elements. (array_splice will remove them from $array.)

$string = implode(', ', array_splice($array, 0, 2));

Add the third one if it's there. (array_shift will remove it from $array too.)

if ($array) {
    $string .= ', and ' . array_shift($array);
}

Count the rest and add them, if any are left.

if ($n = count($array)) {
    $string .= " and $n other". ($n > 1 ? 's' : '');
    //                           conditional pluralization
}
Don't Panic
  • 41,125
  • 10
  • 61
  • 80
1

There are several ways to do it, one simple way :

First you should get the numbers of elements in your array to see if it's more than 3 or not.

$count = count( $array) ;

Then if it's more than 3 , you can make your string:

if($count>3){
    $string= $array[0] . 'and' . $array[1] . 'and' . $array[2] . 'and' . ($count - 3) . "others";
}else{
    $last = array_pop( $array );
    $string = count( $array ) ? implode( ", ", $array ) . " and " . $last : $last;
    //I used exactly your code , you have several ways to do it
    //Attention ... This code may not work when your array has only one element
}
printf( 'Authors: %s', $string );

If you want to know more about PHP arrays you can use this link , a quick tutorial

Or the documentation

M4HdYaR
  • 1,124
  • 11
  • 27
  • 1
    I think there's something wrong with your code it's print to me: Authors: -3others. Can you add your full code? – John Sep 05 '18 at 22:17
  • 1
    Could used in the first response: `$string= $array[0] . ' and ' . $array[1] . ' and ' . $array[2] . ' and ' . ($count - 3) . ' others '` variable `$others`not needed. Just `()`missing arround to `$count - 3` – Jérôme Teisseire Sep 05 '18 at 22:24
  • 1
    M4HdYaR Thank you I got to know what you are trying to do in your answer and this helps me to improve your answer more. @JérômeTeisseire thank you i have done this before your comment :). – John Sep 05 '18 at 22:34
  • This answer does not produce the desired output. – mickmackusa Sep 05 '18 at 22:41
1

I suggest you to think your problem as a function that you can Unit-Test so it becomes easier to build and you can flexibilize your solution.

  • Use counters to track your array: total, first chunk, tail
  • Consider any special cases like 2 elements, 3 elements, etc.
  • Get the last item with array_pop
  • Use array_slice to get the first chunk from the "take N" argument.
  • Implode and concat as required to get the desired result

Here an example of how it could be.

<?php

function niceAuthorsPrint(array $authors, $takeCount){

  $totalAuthors = count( $authors );
  $tailCount = $totalAuthors - $takeCount;

  $first = array_slice($authors, 0, $takeCount);

  $othersLabel = $tailCount == 1 ? 'other' : 'others';

  $string = implode( ", ", $first );

  if($tailCount > 0){
     $string .= " and " . $tailCount . ' ' . $othersLabel;      
  }

  return $string;
}

// take 3
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 3) ."\n";

// take 4
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 4) ."\n";

// take 5
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 5) ."\n";

?>

Will output:

user1, user2, user3 and 2 others
user1, user2, user3, user4 and 1 other
user1, user2, user3, user4, user5

Working example here


Alternative

An alternative for handling special cases and printing the last item value.

<?php

// array of authors, number of authors to take first
function niceAuthorsPrint(array $authors, $takeCount){

  $totalAuthors = count( $authors );

  if($totalAuthors >= 3 && $takeCount >= $totalAuthors)
  {
      $takeCount = 2;
  }

  if($totalAuthors == 2 && $takeCount >= $totalAuthors)
  {
      $takeCount = 1;
  }

  $last = array_pop($authors);

  $tailCount = $totalAuthors - $takeCount;

  $first = array_slice($authors, 0, $takeCount);

  $othersLabel = $tailCount == 1 ? $last : $tailCount . ' others';

  $string = implode( ", ", $first );

  if($tailCount > 0){
     $string .= " and " . $othersLabel;      
  }

  return $string;
}

// take 3
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 3) ."\n";

// take 4
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 4) ."\n";

// take 5
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 5) ."\n";

// take original count, take first three as default
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3', 'user4', 'user5' ), 5) ."\n";

// take original count, take first three as default
echo niceAuthorsPrint(array( 'user1', 'user2', 'user3'), 3) ."\n";

// take original count, take first three as default
echo niceAuthorsPrint(array( 'user1', 'user2'), 2) ."\n";

Will print:

user1, user2, user3 and 2 others
user1, user2, user3, user4 and user5
user1, user2 and 3 others
user1, user2 and 3 others
user1, user2 and user3
user1 and user2

Feel free to adjust to your needs.

Working code example

alariva
  • 2,051
  • 1
  • 22
  • 37
  • Why you edit your answer? With your answer before any edits, you made absolutely what I want. Just one something you forgot only that for the third user separates it with a word 'and' not a comma like this: user1, user2, and user3 and 2 others. and this case happens only if the count of the array greater than 3 and if equal 3 then the results will be: user1, user2, and user3. – John Sep 05 '18 at 23:50
  • You can see the edits history of the answer. Here the link to the previous code: https://3v4l.org/e76Id – alariva Sep 06 '18 at 00:00
  • I already make the code that I want for my project, I just tell you how to make your answer complete to mark it as the best answer. Your old code working without any issues just complete it if you need as I told you in my comment. Anyway thank you very much for your efforts :) – John Sep 06 '18 at 00:05