PHP是否有类似于JavaScript的Array.prototype.some()函数的等价函数?

47

嗯,看起来http://php.net/manual/zh/function.array-filter.php可能会有用,不过它也返回匹配的元素。 - mech
6个回答

39

它没有包含在内,但很容易创建。这使用了SRFI-1中的anyevery名称,但可以重命名为someall

function array_any(array $array, callable $fn) {
    foreach ($array as $value) {
        if($fn($value)) {
            return true;
        }
    }
    return false;
}

function array_every(array $array, callable $fn) {
    foreach ($array as $value) {
        if(!$fn($value)) {
            return false;
        }
    }
    return true;
}

1
可能更快的方法是使用 function array_every($a, $c) {return array_filter($a, function ($b) use ($c) {return !$c($b);}) == $a;} - cwallenpoole
@cwallenpoole 我进行了基准测试(大概),我的版本平均比 array_filter 快 3.6 倍,即使所有调用都返回 true 并且早期退出不是原因,我的版本平均也快 1.8 倍,这让我感到惊讶。 - Sylwester
@Sylwester 好的,没问题。 - cwallenpoole
@cwallenpoole 如果你在第一次成功时停止,那么如何解析整个数组比简单循环更快呢? - Robo Robok
@RoboRobok 嗯,我建议已经五年了,所以这只是一个猜测。大O应该是一样的,但你关于大Omega和大Theta的说法是正确的。我认为这里的区别在于,过去 array_ 函数应该比使用 foreach 更快。那可能是5.x的事情,或者只是从往日传来的谣言。 - cwallenpoole

34

不,PHP标准库中没有等同于短路的功能。有很多非短路解决方案,其中array_reduce可能是最好的选择:

var_dump(array_reduce([2, 5, 8, 1, 4], function ($isBigger, $num) {
    return $isBigger || $num > 10;
}));

你可能值得实现自己的some/any/all函数,或使用一个提供了类似这样的函数式编程基元集合的库,例如https://github.com/lstrojny/functional-php


3
这将具有与array_filter解决方案几乎相同的性能(尽管它在内存使用方面可能更好)。 - GordonM
2
这与JS Array.some()的行为不完全相同,因为它会在第一个返回true的元素上停止,而不会继续处理其余的元素 ;) - qdev
array_reduce([1, 2, 3], fn($isBigger, $num) => $isBigger || $num > 10); 可以在 PHP 7.4 及以上版本中使用,它是一种更为简洁的语法。 - Bradley

3

有一个函数叫做array_filter(),它会根据给定回调函数的返回值来返回给定数组的子集。如果子集为空,则相当于Some()返回false;如果子集不为空,则相当于Some()返回true。

$unfiltered = [1, 11, 2, 22, 3, 33, 4, 44, 5, 55];
$filtered = array_filter ($unfiltered, function ($elem){
    return $elem > 10;
});

print_r ($unfiltered);
print_r ($filtered);
var_dump (empty ($filtered));

这种方法不会出现短路,性能将与数组的大小成反比。但是,在实际应用中这并不重要,因为在你注意到性能影响之前,数组仍然需要变得非常巨大或数组过滤器需要被多次调用。

如果性能非常重要,那么您将不得不自己循环数组,并在找到匹配项后立即跳出循环。

$biggerThanTen = false;
foreach ($unfiltered as $elem)
{
    if ($elem > 10)
    {
        $biggerThanTen = true;
        break;
    }
}

1
function array_some(array $array, callable $callback) {

        $i = 0;
        $n = count($array);

        while($i<$n && !$callback($array[$i])) {
            $i++;
        }

        return $array[$i] ?? null;
}

如果没有找到符合“回调”函数条件的元素,则该函数返回null。

如果发现符合条件的元素,则返回它所找到的第一个元素。

传递为参数的“回调”函数必须根据元素是否符合条件返回true或false。


0
这里有一个 PHP 函数,它的功能类似于 JavaScript 的 Array.some() 函数。
我相信代码本身已经很清晰了,但如果您有任何问题,请随时问我。
// JavaScript-like Array.some() function
$array_some = function($an_array, $callback_function) {
    $is_data_found = false;
    foreach ($an_array as $an_array_index => $array_item) {
        $is_data_found = $callback_function($array_item, $an_array_index, $an_array);
        if ($is_data_found) return $is_data_found;
    }
    return $is_data_found;
};

在普通数组中的使用:

$numbers = [12, 34, 27, 23, 65, 93, 36, 87, 4, 254];

$is_any_number_less_than500 = $array_some($numbers, fn($number) => $number < 500) === false ? 'false' : 'true';
echo("<pre>is any number < 500: $is_any_number_less_than500</pre><br />");
// is any number < 500: true

$is_any_number_more_than500 = $array_some($numbers, fn($number) => $number > 500) === false ? 'false' : 'true';
echo("<pre>is any number > 500: $is_any_number_more_than500</pre><br /><br />");
// is any number > 500: false

在关联数组中的使用:
(类似 JavaScript 对象数组 / 类似 JavaScript JSON)

$products = [
    ['id' => 'id_1', 'price' => 30],
    ['id' => 'id_2', 'price' => 233],
    ['id' => 'id_3', 'price' => 5],
    ['id' => 'id_4', 'price' => 499]
];

$is_any_product_price_less_than500 = $array_some($products, fn($product) => @$product['price'] < 500) === false ? 'false' : 'true';
echo("<pre>is any product price < 500: $is_any_product_price_less_than500</pre><br />");
// is any product price < 500: true

$is_any_product_price_more_than500 = $array_some($products, fn($product) => @$product['price'] > 500) === false ? 'false' : 'true';
echo("<pre>is any product price > 500: $is_any_product_price_more_than500</pre><br /><br />");
// is any product price > 500: true

使用操作数据:

$is_any_number_bigger_than10 = $array_some([2, 5, 8, 1, 4], fn($number) => $number > 10) === false ? 'false' : 'true';
echo("<pre>is any number > 10: $is_any_number_bigger_than10</pre><br />");
// is any number > 10: false

$is_any_number_bigger_than10 = $array_some([12, 5, 8, 1, 4], fn($number) => $number > 10) === false ? 'false' : 'true';
echo("<pre>is any number > 10: $is_any_number_bigger_than10</pre><br /><br />");
// is any number > 10: true

0
使用array_filter并提供回调函数。将其包装在另一个函数中,以计算是否有任何结果。
function array_some(array $data, callable $callback) {
    $result = array_filter($data, $callback);
    return count($result) > 0;
}

$myarray = [2, 5, 8, 12, 4];
array_some($myarray, function($value) {
    return $value > 10;
}); // true

2
是的,我想到了这个,但是觉得可能有点低效,因为array_filter返回一个数组,而我们立即将其丢弃。 - Rory
2
它也不会短路,这可能非常低效。 - deceze
1
这是最简洁的解决方案,因为没有本地函数可以做到这一点。唯一的其他方法是迭代并检查每个值...在最早的机会中中断并返回true。 - R. Chappell

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