array_reduce

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

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

描述

array_reduce(array $array, callable $callback, mixed $initial = null): mixed

array_reduce() 迭代地将 callback 函数应用于 array 的元素,以便将数组缩减为单个值。

参数

array

输入数组。

callback
callback(mixed $carry, mixed $item): mixed
carry

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

item

保存当前迭代的值。

initial

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

返回值

返回结果值。

如果数组为空且未传递 initial,则 array_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) "没有数据可缩减"
?>

参见

添加注释

用户贡献的注释 20 个注释

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

回调的第一个参数是一个累加器,其中有效地组装了正在进行的结果。如果您提供 $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;
}
?>
72
directrix1 at gmail dot com
8 年前
所以,如果您想知道如何在键和值传递给函数的情况下使用它。我使用以下方法取得了成功(此示例从属性 => 值对的关联数组生成格式化的 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"
57
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',
),
),
),
)
3
Julian Sawicki
4 年前
数组 reduce 提供了一种转换数据的方式。
请查看下面的数组。该数组有 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)
8
849330489 at qq dot com
4 年前
第一个参数 $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
2
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);

?>

输出应该是这样的

Array
(
[H] => 1
[e] => 1
[l] => 2
[o] => 1
)
13
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)
13
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 不是一个简单的标量值,您可能会意外地更改它,因此尽管有示例,我还是建议不要完全写入它。
9
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);
?>
14
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)。现在,如果我要执行或Func(),我将不得不设置 `$initial = false`。小心。

请注意,示例中的“rmul”案例巧妙地隐藏了此缺陷!他们使用 $initial 为 10 来获取 `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 从一个 null 参数开始。对回调的第一次调用应该使用参数 ($initial[0],$initial[1])(或者任何前两个数组条目),而不是 (null,$initial[0])。这就是人们从描述中所期望的。

顺便说一句,这也意味着在当前实现下,您将执行 `count($input)` 次对回调的调用,而不是 `count($input) - 1`,正如您所期望的那样。
3
cwu at nolo dot com
8 年前
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;
?>
3
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";
?>
2
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
?>

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

<?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);
}
?>
1
Seanj.jcink.com
18 年前
bishop 发布的用于计算数组字符的代码,对我来说简直是...呃...毫无用处...

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

有效;而且更小。可能也更快。
1
kon
11 年前
使用 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
)
);
?>
2
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'] ]
0
wallfur at gmail dot com
15 天前
如果您需要在回调函数中访问数组的键,可以使用以下替代方案,它使用与 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;
}
-7
galley dot meng at gmail dot com
7 年前
如果您的数组具有字符串键,可以使用 array_reduce、array_merge 和 array_values 将二维数组缩减为一维数组。(PHP>=5.3.0)

<?php

$two_dimensional
= array();

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

$one_dimensional = array_reduce($two_dimensional, 'array_merge', array());

$one_dimensional = array_reduce($two_dimensional, function ($one_dimensional, $value) {
return
array_merge($one_dimensional, array_values($value));
}, array());

# 变为 array(1, 2, 3, 4, 5, 6)
-18
aiadfaris at yahoo dot de
10 年前
注意 array_reduce() 函数
我认为示例 1 中的 rsum 函数不正确,
但是
$ v + = $ w;
将输出 15
-18
aiadfaris at yahoo dot de
10 年前
注意 array_reduce() 函数
我认为示例 1 中的 rsum 函数不正确,
但是
$ v + = $ w;
将输出 15
To Top