在循环时将唯一的值推入数组

61

我正在使用以下循环将项目添加到数组中。我想知道是否有可能在值已经存在于数组中时不将$value添加到$liste数组中?

$liste = array();
foreach($something as $value){
     array_push($liste, $value);
}

这个描述有点含糊不清。它有点取决于$value是什么。它是一个标量值吗?它始终是一个整数吗?还是一个字符串?根据这个重要的细节,可能会有比进行迭代调用in_array()更有效的方法。 - mickmackusa
10个回答

121

在推入数据之前,您可以使用in_array检查其是否已存在。

foreach($something as $value){
    if(!in_array($value, $liste, true)){
        array_push($liste, $value);
    }
}

,true启用了“严格检查”,这会使用===而不是==来比较元素。


3
请注意,如果$value是数字,并恰好与$liste中的一个(数字)键相同,则in_array()可能会返回true,即使在数组中没有这样的值。我建议改用array_search()函数并将结果与false进行强类型比较。 - Jazz
4
@Jazz: 你在说什么?in_array只搜索数组的值,而不是键。http://ideone.com/JjkuP - gen_Eric
1
@Jazz:这从来不是问题,即使在PHP 4中也不是。问题可能出在你的代码上。 - gen_Eric
@Jazz:嘿,有趣。可以通过在in_array中添加,true来启用“严格检查”来解决这些问题。此外,这些问题与数组中的键值无关,而与PHP的奇怪的“类型转换”有关。另外,array_search也会出现相同的问题。 - gen_Eric
3
@Rocket,我回去看了一下。你说得对,我曲解了in_array()的问题 - 它不是与数字键有问题,而是与弱类型转换有问题。我的错误!在我之前使用它的代码中,严格模式不是一个选项(我需要'2'2相同而与'2thing'不同)。我仍然建议新的PHP开发人员使用array_search(),因为它比in_array()具有更少的“陷阱”。教训是 - 无论您使用什么,确保您知道何时它不起作用。 - Jazz
显示剩余3条评论

31

有两个选项。

选项1:对于每个项进行检查,如果已存在该项,则不推入。基本上就是你所要求的:

foreach($something as $value) {
    if( !in_array($value,$liste)) array_push($liste,$value);
}

选项2:将它们添加到列表中,然后在之后去重:

foreach($something as $value) {
    array_push($liste,$value);
}
$liste = array_unique($liste);

从外表看,您可能只是在寻找$liste = array_unique($something);


3
这两个选项哪一个更有效率? - kojow7
@kojow7 看起来这取决于重复元素的概率。如果元素重复的概率高于不重复的概率,那么在我看来第一种选项肯定更有效率。然而,如果情况相反,那么第二种选项看起来更轻量级。 - Omar Trkzi

11

由于这个问题最初使用的不是最佳实践代码,因此我发现这里所有的答案都过于复杂。

解决问题的代码:

基本上,在问题本身中尝试过滤重复项。更好的方法:

$liste = array_unique($something);

如果向一个非空数组中添加元素:

$liste = array_unique(array_merge($liste, $something));

如果您正在使用array_push()函数:

除非您实际上需要使用array_push()的返回值,否则您应该使用:

$liste[] = $value;
为了缩短语法并略微提高性能。

喜欢这种简洁/优美的写法。另一个一行代码可能是:array_push($liste,...array_diff($something,$liste));(不知道这在性能上如何比较)。 - Headbank

5
也许你想将它作为关联数组使用。它的实现方式类似于哈希表,因此插入时间是常量级别的,而不是线性级别的。
function find_uniq( $something ) {
    foreach($something as $value){
         $liste[$value]++;
    }
    return array_keys( $liste );
}

如果你想抑制警告,请在第三行加上@符号。

如果你想避免警告,你需要先检查是否存在:

function find_uniq( $something ) {
    foreach($something as $value){
      if (isset($liste[$value]))
        $liste[$value]++;
      else
        $liste[$value] = 1;
    }
    return array_keys( $liste );
}

5
正确的答案更简单。将所有内容推送,然后使用array_unique()函数:
array_push($array, $new_member);
$array = array_unique($array);

3

在调用array_push()之前,您可以简单地检查此条件。使用array_search()并使用强比较false来查看值是否存在:

foreach( $something as $value ){
    if( array_search( $value, $liste, true ) === false ){
        array_push( $liste, $value );
    }
}

(顺便提一句:在使用“array_search”时,添加,true可以使用“严格检查”。这将使用===而不是==进行比较。)

我不知道你从哪里听到了关于in_array的信息,但那是完全错误的。in_array仅搜索,而不是。http://ideone.com/JjkuP - gen_Eric
1
@Rocket,我看到了你的结果,但是在过去我收到了不同的结果。可能是在5.2.11之前就已经修复了这个问题。 - Jazz
@Jazz:你的代码一定出了什么问题,这从来不是一个问题,即使在PHP 4中也不是。 - gen_Eric
@Rocket,针对那些在使用in_array()函数时遇到问题的人,请参考此链接此链接。我可以向你保证,在三年前我遇到这个问题时,我非常仔细地验证了一下,确认问题并不在我的代码中。 - Jazz
@Jazz:这些问题与数组中的键值无关,而是与PHP的奇怪“类型转换”有关。array_search也会出现相同的问题。您需要添加,true以启用“严格检查”来解决此问题。否则它将使用==进行比较(例如'2dudes' == 2)。 - gen_Eric
@Jazz:我根据我们的讨论编辑了你的答案 :-) - gen_Eric

2
我有另一个解决方案给你!你可以将键用作值,键将永远不会重复
$arr = ["A" => true, "B" => true, "C" => true];

$item = "A";
$arr[$item] = true;

使用方法:

foreach($arr as $value => $helper){
    echo $value;
}

集合类

好的,让我为您编写一个类!不允许重复项的数组被称为集合。

class Set{
    private $countainer;

    public function get(){
        return $this->container;
    }
    public function push($item){
        $this->container[$item] = true;
    }
    public function delete($item){
        unset($this->container[$item]);
    }
}

注意:此方法不适用于关联数组和值。最初的回答。

1
保持无逻辑性可以节省逻辑并提高速度。只需不断覆盖即可。
$list = array();
foreach ($something as $value) {
  if (!is_object($value) && !is_array($value)) {
    $list[$value] = $value
  }
}
$list = array_values($list);

只要$value(作为键)不是对象或数组,这个程序就可以正常工作。 - gradosevic
1
这是一个相当不错的解决方案,但你可以看到它不再是一个无逻辑的解决方案了,对吧?在最新的编辑后,这似乎不再是最优雅的解决方案了。 - David
这是真的,现在每个循环都包含一些逻辑 :( 我需要看一些性能测试才能知道这是否是正确的方法。 - doublejosh
虽然这样做可能很有效,但在某些边缘情况下,键碰撞会损坏数据。例如,浮点数将被截断为整数--因此可能会丢失数据。is_scalar()可以提高“优雅性”。 - mickmackusa

1
伟大的答案已经在上面了,但如果您在代码中有多个array_push(),每次编写if(in_array())语句都会很麻烦。
这里有一个解决方案,可以缩短您的代码: 使用单独的函数。
function arr_inserter($arr,$add){ //to prevent duplicate when inserting
    if(!in_array($add,$arr))
        array_push($arr,$add);
    return $arr;
}

然后在你所有的array_push()需求中,你可以像这样调用上述函数:

$liste = array();
foreach($something as $value){
    $liste = arr_inserter($liste, $value);
}

如果$value已经存在,$liste保持不变。
如果$value尚未存在,则将其添加到$liste中。
希望这有所帮助。

0

您可以使用值作为临时键,只要$something数组不包含以下内容:

  1. 非标量值(对象/数组等不能用作键)或
  2. 在用作数组键时将被改变的值(浮点数被截断,null变为空字符串,布尔值变为整数)
  3. 数字字符串和整数的混合,它们将被松散地评估为相等(2 vs "2"

错误演示


代码:(好的演示)

$something = [3, 2, 1, 3, 6, 5, 1, 1];
$liste = [];
foreach ($something as $value) {
     $liste[$value] = $value;
}

输出:

array (
  3 => 3,
  2 => 2,
  1 => 1,
  6 => 6,
  5 => 5,
)

以上方法绝对比本页其他使用重复检查技术的方法表现更好。PHP不允许在数组的任何单个级别上出现重复键,因此它只会覆盖重新遇到的值。
如果新键不影响未来处理,请将它们保留原样,否则调用array_values()重新索引数组。

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