2024年PHP开发者大会日本站

array_reduce

(PHP 4 >= 4.0.5, PHP 5, PHP 7, PHP 8)

array_reduce使用回调函数迭代地将数组归约为单个值

描述

array_reduce(数组 $array, 回调函数 $callback, 混合类型 $initial = null): 混合类型

array_reduce()callback函数迭代地应用于array的元素,从而将数组归约为单个值。

参数

array

输入数组。

callback
回调函数(混合类型 $carry, 混合类型 $item): 混合类型
carry

保存上一次迭代的返回值;如果是第一次迭代,则保存initial的值。

item

保存当前迭代的值。

initial

如果提供了可选的initial,它将在过程开始时使用,或者在数组为空的情况下用作最终结果。

返回值

返回结果值。

如果数组为空且未传递initialarray_reduce() 返回null

变更日志

版本 描述
8.0.0 如果callback期望通过引用传递参数,此函数现在将发出E_WARNING

示例

示例 #1 array_reduce() 示例

<?php
function sum($carry, $item)
{
$carry += $item;
return
$carry;
}

function
product($carry, $item)
{
$carry *= $item;
return
$carry;
}

$a = array(1, 2, 3, 4, 5);
$x = array();

var_dump(array_reduce($a, "sum")); // int(15)
var_dump(array_reduce($a, "product", 10)); // int(1200), 因为:10*1*2*3*4*5
var_dump(array_reduce($x, "sum", "没有数据可以归约")); // string(17) "没有数据可以归约"
?>

参见

添加注释

用户贡献的注释 17 条注释

Hayley Watson
17 年前
为了更清楚地说明回调函数的两个参数的作用,以及“归约为单个值”的实际含义(使用关联和交换运算符作为示例可能会模糊这一点)。

回调函数的第一个参数是一个累加器,其中有效地组装了进行中的结果。如果提供 $initial 值,则累加器将以此值开始,否则将以 null 开始。
第二个参数是在归约的每个步骤中传递数组的每个值的变量。
回调函数的返回值将成为累加器的新的值。当数组用尽时,array_reduce() 返回累加的值。

如果手动执行归约,则会得到如下几行,每行都会产生相同的结果
<?php
array_reduce
(array(1,2,3,4), 'f', 99 );
array_reduce(array(2,3,4), 'f', f(99,1) );
array_reduce(array(3,4), 'f', f(f(99,1),2) );
array_reduce(array(4), 'f', f(f(f(99,1),2),3) );
array_reduce(array(), 'f', f(f(f(f(99,1),2),3),4) );
f(f(f(f(99,1),2),3),4?>

如果将函数 f($v,$w){return "f($v,$w)";} 设置为最后一行,则最后一行将是字面结果。

因此,PHP 实现可能如下所示(省略错误检查等细节)
<?php
function array_reduce($array, $callback, $initial=null)
{
$acc = $initial;
foreach(
$array as $a)
$acc = $callback($acc, $a);
return
$acc;
}
?>
directrix1 at gmail dot com
9年前
所以,如果你想知道如何在传递键和值到函数的情况下使用它,我已经成功地使用了以下方法(此示例从属性 => 值对的关联数组生成格式化的HTML属性)

<?php

// 属性列表
$attribs = [
'name' => 'first_name',
'value' => 'Edward'
];

// 用于HTML元素内部的格式化属性字符串
$formatted_attribs = array_reduce(
array_keys($attribs), // 我们在这里传入array_keys而不是数组
function ($carry, $key) use ($attribs) { // ...然后我们在这里“使用”实际的数组
return $carry . ' ' . $key . '="' . htmlspecialchars( $attribs[$key] ) . '"';
},
''
);

echo
$formatted_attribs;

?>

这将输出
name="first_name" value="Edward"
souzacomprog at gmail dot com
4年前
有时我们需要遍历数组并对索引进行分组,以便在迭代中更容易提取它们。

<?php

$people
= [
[
'id' => 1, 'name' => 'Hayley'],
[
'id' => 2, 'name' => 'Jack', 'dad' => 1],
[
'id' => 3, 'name' => 'Linus', 'dad'=> 4],
[
'id' => 4, 'name' => 'Peter' ],
[
'id' => 5, 'name' => 'Tom', 'dad' => 4],
];

$family = array_reduce($people, function($accumulator, $item) {
// 如果你没有父亲,你可能是一个父亲
if (!isset($item['dad'])) {
$id = $item['id'];
$name = $item['name'];
// 如果你已经有孩子,就带上他们
$children = $accumulator[$id]['children'] ?? [];
// 添加父亲
$accumulator[$id] = ['id' => $id, 'name' => $name,'children' => $children];
return
$accumulator;
}

// 如果你还没有,添加一个新的父亲
$dad = $item['dad'];
if (!isset(
$accumulator[$dad])) {
// 如何找到父亲,首先只添加有孩子的父亲
$accumulator[$dad] = ['children' => [$item]];
return
$accumulator;
}

// 将儿子添加到已经添加的父亲
// 通过第一个或第二个条件“if”

$accumulator[$dad]['children'][] = $item;
return
$accumulator;
}, []);

var_export(array_values($family));

?>

输出

array (
0 =>
array (
'id' => 1,
'name' => 'Hayley',
'children' =>
array (
0 =>
array (
'id' => 2,
'name' => 'Jack',
'dad' => 1,
),
),
),
1 =>
array (
'id' => 4,
'name' => 'Peter',
'children' =>
array (
0 =>
array (
'id' => 3,
'name' => 'Linus',
'dad' => 4,
),
1 =>
array (
'id' => 5,
'name' => 'Tom',
'dad' => 4,
),
),
),
)

<?php
$array
= [
[
"menu_id" => "1",
"menu_name" => "Clients",
"submenu_name" => "Add",
"submenu_link" => "clients/add"
],
[
"menu_id" => "1",
"menu_name" => "Clients",
"submenu_name" => "List",
"submenu_link" => "clients"
],
[
"menu_id" => "2",
"menu_name" => "Products",
"submenu_name" => "List",
"submenu_link" => "products"
],
];

// 将子菜单分组到它们的菜单中

$menu = array_reduce($array, function($accumulator, $item){
$index = $item['menu_name'];

if (!isset(
$accumulator[$index])) {
$accumulator[$index] = [
'menu_id' => $item['menu_id'],
'menu_name' => $item['menu_name'],
'submenu' => []
];
}

$accumulator[$index]['submenu'][] = [
'submenu_name' => $item['submenu_name'],
'submenu_link' => $item['submenu_link']
];

return
$accumulator;
}, []);

var_export(array_values($menu));

?>

输出

array (
0 =>
array (
'menu_id' => '1',
'menu_name' => 'Clients',
'submenu' =>
array (
0 =>
array (
'submenu_name' => 'Add',
'submenu_link' => 'clients/add',
),
1 =>
array (
'submenu_name' => 'List',
'submenu_link' => 'clients',
),
),
),
1 =>
array (
'menu_id' => '2',
'menu_name' => 'Products',
'submenu' =>
array (
0 =>
array (
'submenu_name' => 'List',
'submenu_link' => 'products',
),
),
),
)
itsunclexo at gmail dot com
2年前
让我们来看一个使用array_reduce()获取字母频率的例子。

<?php

$items
= "Hello";

$frequencies = array_reduce(str_split($items),
function(
$result, $item) {
if (isset(
$result[$item])) {
$result[$item] += 1;
} else {
$result[$item] = 1;
}
return
$result;
},
[]
// 注意初始值是一个数组
);

print_r($frequencies);

?>

输出结果如下:

数组
(
[H] => 1
[e] => 1
[l] => 2
[o] => 1
)
Julian Sawicki
5年前
数组规约提供了一种转换数据的方法。
请查看下面的数组。该数组包含4个嵌套数组。
嵌套数组具有相同的键,只有值不同。

这段代码转换整个数组。请见下文。

$array = array(
0 => array('id' => '100', 'name' => 'Henk', 'age' => '30'),
1 => array('id' => '101', 'name' => 'Piet', 'age' => '33'),
2 => array('id' => '102', 'name' => 'Wim', 'age' => '43'),
3 => array('id' => '103', 'name' => 'Jaap', 'age' => '53'),
);

$arr = array_reduce($array, function($carry, $item){

$arr = array(
'id' => $item['id'],
'value' => $item['name'],
);

$id = $item['id'];
$carry[$id] = $arr;

return $carry;
}, array());

var_dump($arr);


// 输出

array (size=4)
100 => array (size=2)
'id' => string '100' (length=3)
'value' => string 'Henk' (length=4)
101 => array (size=2)
'id' => string '101' (length=3)
'value' => string 'Piet' (length=4)
102 => array (size=2)
'id' => string '102' (length=3)
'value' => string 'Wim' (length=3)
103 => array (size=2)
'id' => string '103' (length=3)
'value' => string 'Jaap' (length=4)
849330489 at qq dot com
5年前
第一个参数$array也可以是函数,这会产生非常有趣和强大的结果,可以用来创建中间件的联合。

<?php

$f1
= function($x, $f){
echo
'中间件1开始。'.PHP_EOL;
$x += 1;
$x = $f($x);
echo
'中间件1结束。'.PHP_EOL;
return
$x;
};


$f2 = function($x, $f){
echo
'中间件2开始:'.PHP_EOL;
$x += 2;
$x = $f($x);
echo
'中间件2结束。'.PHP_EOL;
return
$x;
};

$respond = function($x){
echo
'生成一些响应。'.PHP_EOL;
return
$x;
};

$middlewares = [$f1, $f2];
$initial = $respond;
$foo = array_reduce($middlewares, function($stack, $item){
return function(
$request) use ($stack, $item){
return
$item($request, $stack);
};
},
$initial);

$x = 1;
echo
$foo($x);

?>

//输出
中间件2开始
中间件1开始。
生成一些响应。
中间件1结束。
中间件2结束。
4
magnesium dot oxide dot play+php at gmail dot com
10年前
可以使用`array_reduce`和`array_merge`将二维数组简化为一维数组。(PHP>=5.3.0)

<?php

$two_dimensional
= array();
$two_dimensional['foo'] = array(1, 2, 3);
$two_dimensional['bar'] = array(4, 5, 6);

$one_dimensional = array_reduce($two_dimensional, 'array_merge', array());
# 变为 array(1, 2, 3, 4, 5, 6)
Altreus
10年前
你可以有效地忽略`$result`以引用方式传递到回调函数的事实。只有回调函数的返回值会被考虑。

<?php

$arr
= [1,2,3,4];

var_dump(array_reduce(
$arr,
function(&
$res, $a) { $res += $a; },
0
));

# NULL

?>

<?php

$arr
= [1,2,3,4];

var_dump(array_reduce(
$arr,
function(
$res, $a) { return $res + $a; },
0
));

# int(10)
?>

但是需要注意的是,如果`$res`不是简单的标量值,你可能会意外地改变它,所以尽管有这些例子,我还是建议不要写入它。
ruslan dot zavackiy at gmail dot com
12年前
如果你想要在处理数组规约时使代码更优雅,只需移除第一个元素,并将其用作初始值,因为如果不这样做,你将把第一个元素与自身相加。

<?php
$arr
= array(
array(
'min' => 1.5456, 'max' => 2.28548, 'volume' => 23.152),
array(
'min' => 1.5457, 'max' => 2.28549, 'volume' => 23.152),
array(
'min' => 1.5458, 'max' => 2.28550, 'volume' => 23.152),
array(
'min' => 1.5459, 'max' => 2.28551, 'volume' => 23.152),
array(
'min' => 1.5460, 'max' => 2.28552, 'volume' => 23.152),
);

$initial = array_shift($arr);

$t = array_reduce($arr, function($result, $item) {
$result['min'] = min($result['min'], $item['min']);
$result['max'] = max($result['max'], $item['max']);
$result['volume'] += $item['volume'];

return
$result;
},
$initial);
?>
php at keith tyler dot com
14年前
如果不提供`$initial`,则迭代中使用的第一个值将为NULL。对于将NULL视为单位元素的回调函数(例如加法),这不是问题,但对于NULL不是单位元素的情况(例如布尔上下文),则会成为问题。

比较

<?php
function andFunc($a, $b) {
return
$a && $b;
}
$foo = array(true, true, true);
var_dump(array_reduce($foo, "andFunc"));
?>

返回false!人们会期望它返回true,因为`true && true && true == true`!

向andFunc()添加诊断输出显示,第一次调用andFunc的参数为(NULL, true)。这解析为false(因为`(bool) null == false`),从而破坏了整个归约过程。

所以在这种情况下,我必须设置`$initial = true`,以便第一次调用andFunc()的参数为(true, true)。现在,如果我使用的是orFunc(),则必须设置`$initial = false`。注意。

请注意,示例中的“rmul”案例巧妙地隐藏了这个缺陷!他们使用10作为`$initial`来得到`10*1*2*3*4*5 = 12000`。因此,你会认为如果没有初始值,你会得到`1200/10 = 120 = 1*2*3*4*5`。不!你得到一个很大的零,因为`int(null)==0`,并且`0*1*2*3*4*5 = 0`!

我不明白为什么`array_reduce`以一个空参数开始。回调函数的第一次调用应该使用参数($initial[0],$initial[1])(或任何前两个数组条目),而不是(null,$initial[0])。根据描述,人们会期望如此。

顺便说一句,这也意味着在当前实现下,你将调用回调函数`count($input)`次,而不是你可能期望的`count($input) - 1`次。
cwu at nolo dot com
9年前
`array_reduce()`返回的单个值可以是一个数组——如下例所示
<?php
# 计算数组的平均值
function calculate_sum_and_count($sum_and_count, $item)
{
list(
$sum, $count) = $sum_and_count;
$sum += $item;
$count += 1;
return [
$sum, $count];
}

$a = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
$initial_sum_and_count = [0, 0];
list(
$sum, $count) = array_reduce($a, "calculate_sum_and_count", $initial_sum_and_count);
echo
$sum / $count;
?>
bdechka at yahoo dot ca
17 年前
上面的代码这样写更好。

<?php
function reduceToTable($html, $p) {
$html .= "<TR><TD><a href=\"$p.html\">$p</a></td></tr>\n";
return
$html;
}

$list = Array("page1", "page2", "page3");

$tab = array_reduce($list, "reduceToTable");
echo
"<table>".$tab . "</table>\n";
?>
yuki [dot] kodama [at] gmail [dot] com
17 年前
这段代码会深度归约数组。

<?php
function print_s($s) {
return
is_null($s) ? "NULL" : (is_array($s) ? "Array" : ($s ? "TRUE" : "FALSE"));
}
function
r_and_dp($a, $b) {
echo
"phase1:" . print_s($a) . "," . print_s($b) . "<br>\n";
if(
is_array($a)) {
$a = array_reduce($a, "r_and_dp");
}
if(
is_array($b)) {
$b = array_reduce($b, "r_and_dp");
}
echo
"phase2:" . print_s($a) . "," . print_s($b) . "<br>\n";
$a = is_null($a) ? TRUE : $a;
$b = is_null($b) ? TRUE : $b;
echo
"phase3:" . print_s($a) . "," . print_s($b) . "<br>\n";
return
$a && $b;
}
$bools = array(TRUE, array(FALSE, TRUE), TRUE);
echo
print_s(array_reduce($bools, "r_and_dp")) . "<br>\n";

// 结果:FALSE
?>

使用布尔值时,必须仔细设置“initial”参数。

<?php
function r_or_dp($a, $b) {
if(
is_array($a)) {
$a = array_reduce($a, "r_or_dp");
}
if(
is_array($b)) {
$b = array_reduce($b, "r_or_dp");
}
return (
is_null($a) ? FALSE : $a) || (is_null($b) ? FALSE : $b);
}
?>
Seanj.jcink.com
18年前
bishop 发布的用于计算数组字符数的代码简直……嗯……对我来说毫无用处……

$array=Array("abc","de","f");
strlen(implode("",$array)); //6

有效;而且更简洁。可能也更快。
kon
12年前
使用`array_reduce`遍历相关对象的属性

<?php
$a
=new stdClass;
$a->b=new stdClass;
$a->b->c="Hello World!\n";

$reductionPath=array("b","c");

print_r(
array_reduce(
$reductionPath,
function(
$result, $item){
return
$result->$item;
},
$a
)
);
?>
xyz at record dot contact
1年前
如果你想重新索引一个数组,例如为每个元素添加一个ID作为键,你可以使用以下方法

$array = [['id' => 3, 'name' => 'bob'], ['id' => 4, 'name' => 'alice']];
array_reduce($array, fn($new_array, $item) => $new_array + [$item['id'] => $item], []);

结果

[ 3 => ["id" => 3, "name" => "bob"], 4 => ['id' => 4, 'name' => 'alice'] ]
wallfur at gmail dot com
4个月前
如果你需要在回调函数中访问数组的键,这个替代方案使用与array_reduce()及其回调函数相同的签名,并增加了$key参数
<?php
function array_reduce_assoc(array $array, callable $callback, mixed $initial = null): mixed
{
foreach (
$array as $key => $item)
{
$initial = call_user_func($callback, $initial, $item, $key);
}
return
$initial;
}
To Top