PHP数组按值删除(而不是键)

1151
我有一个PHP数组,如下所示:
$messages = [312, 401, 1599, 3, ...];

我想删除包含值$del_val(例如$del_val=401)的元素,但我不知道它的键。这可能有所帮助:每个值只能出现一次

请为执行此任务提供最简单的函数。


1
但是,如果您在此数组上有许多删除操作,那么一次迭代并使其键与值相同是否更好? - dzona
1
同样的问题:https://dev59.com/WHI-5IYBdhLWcg3wYnSQ - Eric Lavoie
可能是PHP从数组中删除元素的重复问题。 - Alex
21个回答

1988
使用 array_search()unset,尝试以下操作:
if (($key = array_search($del_val, $messages)) !== false) {
    unset($messages[$key]);
}

array_search() 返回找到的元素的键,可以使用该键从原始数组中删除该元素,使用unset()。如果失败,它将返回FALSE,但是它可以在成功时返回一个假值(例如,您的键可能是0),这就是为什么使用严格比较!==运算符。

if()语句将检查array_search()是否返回了一个值,并且只有在返回值时才执行操作。


17
使用 $messages = array_diff($messages, array($del_val)) 能否达到同样的效果?在性能方面会更好吗? - Adam Strudwick
10
为什么不试一下呢?我的感觉是 array_diff() 会更慢,因为它需要比较 两个 数组,而不仅仅像 array_search() 那样搜索 一个 数组。 - Bojangles
26
尽管这是有效的,但你应该避免在类似那样的语句中赋值。这只会给你带来麻烦。 - adlawson
17
如果你要查找的值具有键 0 或任何其他 falsy 值,则它不会取消设置该值,你的代码将无法正常工作。你应该测试 $key === false。(编辑 - 你明白了) - evan
6
请注意,这不会重置数组键。 - montrealist
显示剩余16条评论

916

删除数组中的一个元素基本上就是将带有该元素的差集设置为:

difference
array_diff( [312, 401, 15, 401, 3], [401] ) // removing 401 returns [312, 15, 3]

它的泛化效果很好,如果需要,您可以同时删除任意数量的元素。

免责声明:请注意,与被接受的答案(会改变原数组)不同,我的解决方案在保持旧数组不变的同时生成一个新副本。请选择符合需求的。


39
仅适用于可转换为字符串的对象。 - nischayn22
8
我似乎因为使用了 [$element] 这个语法而出现了“解析错误”,我改用了 array($element)。这不是什么大问题,但我想让其他遇到类似问题的人知道他们并不孤单。 - Angad
9
没问题,我假设 PHP 5.4 现在占主导地位以舍弃旧的符号表示法。感谢您的提醒。 - Rok Kralj
27
值得注意的是,由于某种原因array_diff使用(string) $elem1 === (string) $elem2作为其相等条件,而不是您可能期望的$elem1 === $elem2。@nischayn22指出的问题就是这个条件导致的。如果您想要一个适用于任意元素数组(可能是对象)的实用函数,则Bojangle的答案可能更好。 - Mark Amery
6
请注意,此方法对array_diff()的每个参数执行内部排序,因此将运行时间从O(n)增加到O(n lg n)。 - Ja͢ck
显示剩余11条评论

175
一种有趣的方法是使用array_keys()函数:
foreach (array_keys($messages, 401, true) as $key) {
    unset($messages[$key]);
}

函数array_keys()接受两个额外的参数,以返回特定值的键,并确定是否需要进行严格检查(即使用===进行比较)。

这还可以删除具有相同值的多个数组项(例如[1, 2, 3, 3, 4])。


1
是的,这对于选择多个数组项/键非常有效。 - Oki Erie Rinaldi
4
适用于可能不包含全部独特数值的数组最佳。 - Derokorian
问题在于它使键的索引无序:[0] - a,[2] - b([1] 消失了,但数组仍然缺少它)。 - Rodniko
3
如果你需要保持数组的顺序,那么你需要使用array_values()函数;虽然剩余的键仍然按照原来的顺序排列,所以严格来说它并不是"未排序"。 - Ja͢ck
1
很棒的答案 - 我不知道 array_keys() 可以进行搜索。 - But those new buttons though..

73

如果你确定你的数组只包含一个具有该值的元素,你可以这样做:

$key = array_search($del_val, $array);
if (false !== $key) {
    unset($array[$key]);
}

然而,如果你的值可能在数组中出现多次,你可以这样做

$array = array_filter($array, function($e) use ($del_val) {
    return ($e !== $del_val);
});

注意:第二种选项仅适用于带有闭包的PHP5.3+。


50
$fields = array_flip($fields);
unset($fields['myvalue']);
$fields = array_flip($fields);

15
仅当您的数组中不包含除要删除的值以外的重复值时,此方法才有效。 - jberculo
3
@jberculo,有时这正是你需要的,在某些情况下,这可以节省我进行一次数组去重操作的时间。 - DarkMukke
也许吧,但我会使用专门设计用于此目的的函数,而不是仅仅依赖于一个基本上被用于其他用途的函数的幸运副作用。这样做还可以使您的代码更加透明。 - jberculo
该消息指出“每个值只能出现一次”,这应该有效。如果发帖人使用相同的变量名称并添加一些说明,那就更好了。 - Raatje
2
显然,与所选解决方案相比,这是最快的,我进行了小型基准测试。 - user1642018
显示剩余2条评论

38

使用 PHP 7.4 的箭头函数:

$messages = array_filter($messages, fn ($m) => $m != $del_val);
为了保持非关联数组,需要使用array_values()将其包装:
$messages = array_values(array_filter($messages, fn ($m) => $m != $del_val));

这太棒了。箭头函数很容易理解,实用性强,而且对我来说是新的。我需要根据嵌套值从关联数组的一部分中删除,但其他答案都不太可能做到。我曾经使用foreach()循环,但这种方法更加简洁。 - But those new buttons though..

36

6
这将不适用于关联数组和在其键中存在间隔的数组,例如 [1, 2, 4 => 3] - Ja͢ck
不好意思,这样行不通。请阅读我提供链接的文章。 - Airy
6
不会。考虑我上面评论中的数组;在使用您的代码删除值“3”后,数组将变为“[1, 2, 3]”,换句话说,该值没有被删除。需要明确的是,我并不是在说它在所有情况下都失败,只是在这个例子中失败了。 - Ja͢ck
1
array_splice是最好的方法,unset在删除后不会调整数组索引。 - Raaghu
不是一个好的解决方案。如果你搜索的值不存在,array_search会返回"false",而array_splice会将其转换为0,并且会移除数组中的第一个元素。$array = [3344, 4455, 5566]; array_splice($array, array_search(1122, $array), 1); // 结果为 $array = [4455, 5566] - sNICkerssss

33

或者简单地说,手动方式:

foreach ($array as $key => $value){
    if ($value == $target_value) {
        unset($array[$key]);
    }
}

这是它们中最安全的,因为您可以完全控制您的数组


1
在这种情况下,使用array_splice()而不是unset()将重新排序数组索引,这可能更好。 - Daniele Orlando

19
function array_remove_by_value($array, $value)
{
    return array_values(array_diff($array, array($value)));
}

$array = array(312, 401, 1599, 3);

$newarray = array_remove_by_value($array, 401);

print_r($newarray);

输出

数组 ( [0] => 312 [1] => 1599 [2] => 3 )


2
我不确定这是否更快,因为此解决方案涉及多个函数调用。 - Julian Paolo Dayag
2
在 Stack Overflow 上,仅仅给出代码的回答是没有多大价值的。每个回答都应该包括一个说明,说明它是如何工作的或者为什么你认为它是好的建议,相对于其他技术。 - mickmackusa

16

你可以做:

unset($messages[array_flip($messages)['401']]);

说明:翻转数组后删除具有键 401 的元素。


如果您想要保留状态,就必须非常小心,因为所有未来的代码都必须具有值而不是键。 - saadlulu
2
@saadlulu $messages数组不会被翻转,因为array_flip()函数不会影响原始数组,所以在应用前一行代码后,生成的数组将与原数组相同,只是不需要的结果已被删除。 - Qurashi
4
不确定这是否正确,如果有几个元素的值都是401会怎样? - Zippp
这仍将保留键。 - Szabolcs Páll

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