将数组使用", "分隔符合并,并在最后一项前添加"and"。

91

这个数组包含一些项,我想把它转换成字符串,但是我不知道如何使最后一项在逗号之前变成&/and。

1 => coke 2=> sprite 3=> fanta

应该变成什么?

coke, sprite and fanta

这是常规的implode函数:

$listString = implode(', ', $listArrau);

有没有一个简单的方法?


4
在将其余部分合并之前,为什么不剪切掉数组的最后一个元素呢?然后只需使用 .= 将其连接到字符串中即可。 - djot
16个回答

137

适用于任意数量项目的长代码行:

echo join(' and ', array_filter(array_merge(array(join(', ', array_slice($array, 0, -1))), array_slice($array, -1)), 'strlen'));

或者,如果你真的更喜欢冗长:

$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);

关键在于这种切片、合并、过滤和连接处理所有情况,包括0、1和2项,而无需额外的if..else语句。而且它可以折叠为一行代码。


24
+1表示将所有内容放在一行,-1表示5个月后需要回来并弄清楚它尝试做什么。 - Mike Purcell
3
聪明的想法,但不是最易读的解决方案 :P。 - Interrobang
4
如果您希望的话,可以将其拆分为单个步骤,但是这种方法不需要任何ifs或循环,并且可以在一个表达式中完成。这就是我称之为长行的原因。当然,您可以将其放入名为joinWithLastSeparator(array $array, $separator = ' and ')的函数中,以便在5个月后仍知道它的作用。:o) - deceze
6
五年过去了,我不得不承认我可能会对自己的回答点踩...我仍然为这个事实感到自豪,因为它只是一个表达方式,但是...嗯,是的...;o) - deceze
2
不要听单行代码反对者的话。所有这些抱怨都可以通过将调用抽象成一个命名良好的函数来轻松处理。StringUtils::implodeWithPrefixOnLast($array, ', ', ' and '); - Nathan
显示剩余6条评论

114

我不确定一行代码是解决这个问题最优雅的方法。

我写了这个一段时间了,根据需要随时使用:

/**
 * 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;
}

你不必使用“and”作为连接字符串,它可以有效地处理从0到无限数量的任何内容:

// 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'));

如果您想要,很容易将其修改以包含牛津逗号:

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;
}

+1 针对该解决方案。易读且良好。但是,如果数组没有元素,返回 null 对我来说有点奇怪。我更喜欢返回空字符串。 - Mitko Delibaltov

31
您可以弹出最后一个项目,然后将其与文本连接起来:
$yourArray = ('a', 'b', 'c');
$lastItem = array_pop($yourArray); // c
$text = implode(', ', $yourArray); // a, b
$text .= ' and '.$lastItem; // a, b and c

1
同时,它不适用于 sizeof($yourArray) == 0。因此,正确的解决方案是先检查数组的长度,然后再执行上面的代码。如果 size==0,则什么也不做;如果 size==1,则返回第一个项目;否则执行上面的代码。 - JercSi
7
当添加条件 if (count($yourArray) > 1) 时,它的效果非常好...谢谢 :) - Enissay
Enrique的解决方案适用于1个元素(和0个元素),无需额外的“if”。 - ChrisV

15

试试这个:

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

1
这个的唯一问题是,如果有三个或更多元素,我想在最后一个元素之前加上逗号,但在某些情况下可能只有两个元素,如果使用相同的代码,则会添加一个错误的逗号。尽管如此,我认为这将在99%的情况下都可以工作,所以+1。 - Eckstein

4

另一个可能的简短解决方案:

$values = array('coke', 'sprite', 'fanta');

$values[] = implode(' and ', array_splice($values, -2));
print implode(', ', $values);  // "coke, sprite and fanta"

它在任何数量的值下都可以正常工作。


3

我的首选是与Enrique的答案类似,但可选择处理牛津逗号。

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;
}

1
这可以使用array_fillarray_map完成。这也是一个一行代码的解决方案(似乎许多人都喜欢它们),但为了易读性进行了格式化:
$string = implode(array_map(
    function ($item, $glue) { return $item . $glue; }, 
    $array,
    array_slice(array_fill(0, count($array), ', ') + ['last' => ' and '], 2)
));

虽然不是最优解,但仍然可行。

这里是演示


1

我知道回答已经晚了,但这肯定是更好的做法吧?

$list = array('breakfast', 'lunch', 'dinner');
$list[count($list)-1] = "and " . $list[count($list)-1];
echo implode(', ', $list);

1
缺点是在最后一个元素(在“and”之前)之前总是有一个逗号。 - ChrisV
这仍然是正确的语法。但是,我同意你的观点。Enrique的答案似乎与我的类似,但解决了这个问题 :) - Jack B
当列表有多个项目时有效,但当列表是动态的时无效。(可以轻松通过添加“if”包装器来解决) - random_user_name

0

好的,这已经变得相当老了,但我必须说,我认为大多数答案都非常低效,需要多个implode或数组合并等复杂操作,比必要的要复杂得多。

为什么不只是:

implode(',', array_slice($array, 0, -1)) . ' and ' . array_slice($array, -1)[0]

如果数组只有一个元素,则此解决方案不适用。 - mickmackusa

0

使用正则表达式进行简单的human_implode

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接