PHP Conference Japan 2024

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,它將在流程開始時使用,或者在陣列為空時作為最終結果。

回傳值

回傳結果值。

如果陣列為空,且未傳遞 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), because: 10*1*2*3*4*5
var_dump(array_reduce($x, "sum", "No data to reduce")); // string(17) "No data to reduce"
?>

參見

新增註解

使用者貢獻的註解 17 個註解

135
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;
}
?>
73
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"
59
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
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
)
3
Julian Sawicki
5 年前
Array 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
5 年前
第一個參數 $array 也可以是函式,這會產生非常有趣且強大的結果,可以用於建立中介軟體的聯集。

<?php

$f1
= function($x, $f){
echo
'middleware 1 begin.'.PHP_EOL;
$x += 1;
$x = $f($x);
echo
'middleware 1 end.'.PHP_EOL;
return
$x;
};


$f2 = function($x, $f){
echo
'middleware 2 begin: '.PHP_EOL;
$x += 2;
$x = $f($x);
echo
'middleware 2 end.'.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);

?>

//輸出
middleware 2 begin
middleware 1 begin.
產生一些回應。
middleware 1 end.
middleware 2 end.
4
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)。現在,如果我正在做,比如說,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 會以 null 引數開始。對回呼函式的第一次呼叫應該使用引數 ($initial[0],$initial[1]) [或是前兩個陣列條目],而不是 (null,$initial[0])。這才符合人們對描述的期望。

附帶一提,這也表示在目前的實作下,您會對回呼函式產生 `count($input)` 次呼叫,而不是您可能預期的 `count($input) - 1` 次。
3
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;
?>
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
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
)
);
?>
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'] ]
1
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