array_map、array_walk和array_filter之间的区别

Difference between array_map, array_walk and array_filter

提问人: 提问时间:8/8/2010 最后编辑:HoldOffHunger 更新时间:6/23/2021 访问量:207644

问:

和 到底有什么区别。我从文档中可以看到,您可以传递回调函数来对提供的数组执行操作。但我似乎没有发现它们之间有什么特别的区别。array_maparray_walkarray_filter

它们执行相同的操作吗?
它们可以互换使用吗?

如果它们完全不同,我将不胜感激您提供说明性示例的帮助。

PHP 数组 回调

评论

0赞 Lance Cleveland 3/9/2013
这是通过 array_reduce() 进行命名数组处理的一个很酷的技巧。如果您正在调查array_map、array_walk和array_filter,值得一读。stackoverflow.com/questions/11563119/......

答:

646赞 Artefacto 8/8/2010 #1
  • 更改值:
  • 阵列键访问:
  • array_map不能使用数组键进行操作,array_walk可以。
  • 返回值:
  • array_map返回一个新数组,array_walk只返回 .因此,如果您不想因遍历一个数组而创建数组,则应使用 array_walktrue
  • 迭代多个数组:
  • array_map还可以接收任意数量的数组,并且可以并行迭代它们,而array_walk只在一个数组上运行。
  • 将任意数据传递给回调:
  • array_walk可以接收一个额外的任意参数来传递给回调。自 PHP 5.3 以来,这几乎无关紧要(当引入匿名函数时)。
  • 返回数组的长度:
  • 生成的数组的长度与最大输入数组的长度相同; 不返回数组,但同时不能改变原始数组的元素数量;array_filter根据过滤函数仅选取数组元素的子集。它确实保留了密钥。array_maparray_walk

例:

<pre>
<?php

$origarray1 = array(2.4, 2.6, 3.5);
$origarray2 = array(2.4, 2.6, 3.5);

print_r(array_map('floor', $origarray1)); // $origarray1 stays the same

// changes $origarray2
array_walk($origarray2, function (&$v, $k) { $v = floor($v); }); 
print_r($origarray2);

// this is a more proper use of array_walk
array_walk($origarray1, function ($v, $k) { echo "$k => $v", "\n"; });

// array_map accepts several arrays
print_r(
    array_map(function ($a, $b) { return $a * $b; }, $origarray1, $origarray2)
);

// select only elements that are > 2.5
print_r(
    array_filter($origarray1, function ($a) { return $a > 2.5; })
);

?>
</pre>

结果:

Array
(
    [0] => 2
    [1] => 2
    [2] => 3
)
Array
(
    [0] => 2
    [1] => 2
    [2] => 3
)
0 => 2.4
1 => 2.6
2 => 3.5
Array
(
    [0] => 4.8
    [1] => 5.2
    [2] => 10.5
)
Array
(
    [1] => 2.6
    [2] => 3.5
)

评论

4赞 feeela 11/27/2012
PHP 手册说:“array_walk():只有数组的值可能会被更改;”
12赞 Jarek Jakubowski 4/12/2016
“array_map无法使用数组键进行操作”,这是不正确的:array_map(callback($key, $value), array_keys($array), $array)
18赞 inarilo 4/13/2017
它仍然没有访问任何数组的键,而是访问您从键创建的数组中的值。这是一种解决方法,它不会否定该声明。
1赞 FantomX1 10/4/2019
虽然array_map不会隐式更改值,但通过将结果分配给同一数组,它基本上会更改它,并且“矛盾”地,对同一数组本身进行操作的array_walk不会直接更改其值,除非通过引用传递值(数组遍历可能会通过传递原始数组的匿名函数使用子句间接删除索引/元素array_filter但这是一种解决方法)。因此,总而言之,更改值,或者是否通过引用返回或传递值,实际上差异较小,但数组遍历适用于索引和具有多个数组的数组映射
0赞 FantomX1 11/19/2019
此外,看起来无论数组遍历将第一个数组参数作为引用,当想要更改它时,他都必须传递回调项值作为引用
45赞 Steven Schlansker 8/8/2010 #2

从文档中,

bool array_walk ( array &$array , callback $funcname [, mixed $userdata ] ) <-return bool

array_walk 接受一个数组和一个函数,并通过用 替换每个元素 x 来修改它。FF(x)

数组array_map(回调$callback, 数组 $arr 1 [, 数组 $... ] )<返回数组

array_map执行完全相同的操作,只是它不会就地修改,而是返回一个包含转换元素的新数组。

array array_filter ( array $input [, callback $callback ] )<-return 数组

array_filter函数 ,而不是转换元素,将删除任何不真实的元素FF(x)

评论

0赞 Dylan Valade 10/8/2016
无法弄清楚为什么我的数组值消失了。查看文档,我假设返回了一个数组,并认为问题出在我的函数中。直到我看到这个才意识到返回类型是布尔值。array_walkarray_map
99赞 Kendall Hopkins 8/8/2010 #3

将函数映射到数据数组的思想来自函数式编程。你不应该把它看作是一个循环,它对数组的每个元素都调用一个函数(即使它是这样实现的)。它应该被认为是将函数独立应用于数组中的每个元素。array_mapforeach

从理论上讲,函数映射之类的事情可以并行完成,因为应用于数据的函数应该只影响数据,而不是全局状态。这是因为可以选择将函数应用于 IN 中的项目的任何顺序(即使在 PHP 中它没有)。array_map

array_walk另一方面,它处理数据数组的方法完全相反。它不是单独处理每个项目,而是使用状态 () 并可以就地编辑项目(很像 foreach 循环)。由于每次应用一个项目时,它都可能改变程序的全局状态,因此需要一种正确的方法来处理项目。&$userdata$funcname

回到PHP领域,它们几乎相同,只是让你对数据的迭代有更多的控制,通常用于就地“更改”数据,而不是返回一个新的“更改”数组。array_maparray_walkarray_walk

array_filter实际上是(或)的应用程序,它或多或少只是为了方便而提供的。array_walkarray_reduce

评论

8赞 etherice 1/2/2014
+1 表示您的第 2 段见解“理论上,函数映射之类的事情可以并行完成,因为应用于数据的函数应该只影响数据,而不是全局状态。对于我们并行程序员来说,这是一件需要牢记的有用事情。
0赞 pfrenssen 7/31/2017
您能解释一下如何使用 ?array_filter()array_walk()
0赞 Kamafeather 7/28/2022
@pfrenssen不可能通过步行实现过滤器;正如 [stackoverflow.com/a/3432266/3088045](@Artefacto 的答案中的“返回数组的长度”中提到的,使用 walk 时无法更改元素的数量。
1赞 slevy1 4/11/2011 #4

下面的修订试图更清楚地描述PHP的array_filer()、array_map()和array_walk(),它们都源自函数式编程:

array_filter() 过滤掉数据,从而生成一个新数组,该数组仅包含前一个数组的所需项目,如下所示:

<?php
$array = array(1, "apples",2, "oranges",3, "plums");

$filtered = array_filter( $array, "ctype_alpha");
var_dump($filtered);
?>

实时代码在这里

所有数值都从$array中过滤掉,只留下$filtered水果类型。

array_map() 也创建一个新数组,但与 array_filter() 不同的是,生成的数组包含输入$filtered的每个元素,但由于对每个元素应用回调,因此值发生了变化,如下所示:

<?php

$nu = array_map( "strtoupper", $filtered);
var_dump($nu);
?>

实时代码在这里

在这种情况下,代码使用内置的 strtoupper() 应用回调,但用户定义的函数也是另一个可行的选项。回调适用于$filtered的每个项目,从而生成其元素包含大写值$nu。

在下一个代码段中,数组 walk() 遍历$nu,并对引用运算符 '&' 的每个元素进行更改。更改不会创建其他数组。每个元素的值都会就地更改为信息量更大的字符串,指定其键、类别和值。

<?php

$f = function(&$item,$key,$prefix) {
    $item = "$key: $prefix: $item";
}; 
array_walk($nu, $f,"fruit");
var_dump($nu);    
?>    

观看演示

注意:关于 array_walk() 的回调函数采用两个参数,当 array_walk() 调用时,它们将自动获取元素的值及其键,并且按此顺序获取。(在此处查看更多内容)。

评论

1赞 Warbo 10/9/2015
请注意,函数 and 只是现有函数的 eta 扩展,因此是完全多余的。您可以通过传递基础函数(名称)来获得相同的结果: 和$lambda$callback$filtered = array_filter($array, 'ctype_alpha');$nu = array_map('strtoupper', $filtered);
23赞 Warbo 5/2/2014 #5

其他答案很好地说明了array_walk(就地修改)和array_map(返回修改后的副本)之间的区别。然而,他们并没有真正提到array_reduce,这是理解array_map和array_filter的一种启发性方式。

array_reduce函数接受一个数组、一个双参数函数和一个“累加器”,如下所示:

array_reduce(array('a', 'b', 'c', 'd'),
             'my_function',
             $accumulator)

使用给定的函数,数组的元素一次与累加器组合一个。上述调用的结果与执行此操作的结果相同:

my_function(
  my_function(
    my_function(
      my_function(
        $accumulator,
        'a'),
      'b'),
    'c'),
  'd')

如果您更喜欢从循环的角度来考虑,就像执行以下操作一样(当array_reduce不可用时,我实际上将其用作后备):

function array_reduce($array, $function, $accumulator) {
  foreach ($array as $element) {
    $accumulator = $function($accumulator, $element);
  }
  return $accumulator;
}

这个循环版本清楚地说明了为什么我将第三个参数称为“累加器”:我们可以使用它来累积每次迭代的结果。

那么这与array_map和array_filter有什么关系呢?事实证明,他们都是一种特殊的array_reduce。我们可以这样实现它们:

array_map($function, $array)    === array_reduce($array, $MAP,    array())
array_filter($array, $function) === array_reduce($array, $FILTER, array())

忽略这样一个事实,即array_map和array_filter以不同的顺序接受他们的论点;这只是PHP的另一个怪癖。重要的一点是,除了我称之为 $MAP 和 $FILTER 的函数之外,右侧是相同的。那么,它们长什么样子呢?

$MAP = function($accumulator, $element) {
  $accumulator[] = $function($element);
  return $accumulator;
};

$FILTER = function($accumulator, $element) {
  if ($function($element)) $accumulator[] = $element;
  return $accumulator;
};

如您所见,这两个函数都接收$accumulator并再次返回。这些函数有两个区别:

  • $MAP 将始终附加到 $accumulator,但仅当 $function($element) 为 TRUE 时,$FILTER才会这样做。
  • $FILTER 附加原始元素,但$MAP附加 $function($element)。

请注意,这远非无用的琐事;我们可以用它来使我们的算法更有效率!

我们经常可以看到以下两个示例的代码:

// Transform the valid inputs
array_map('transform', array_filter($inputs, 'valid'))

// Get all numeric IDs
array_filter(array_map('get_id', $inputs), 'is_numeric')

使用 array_map 和 array_filter 而不是循环使这些示例看起来非常漂亮。但是,如果$inputs很大,则效率可能非常低,因为第一次调用(映射或过滤器)将遍历$inputs并构建中间数组。这个中间数组被直接传递到第二个调用中,它将再次遍历整个过程,然后需要对中间数组进行垃圾回收。

我们可以通过利用 array_map 和 array_filter 都是array_reduce的例子这一事实来摆脱这个中间数组。通过组合它们,我们只需要在每个示例中遍历$inputs一次:

// Transform valid inputs
array_reduce($inputs,
             function($accumulator, $element) {
               if (valid($element)) $accumulator[] = transform($element);
               return $accumulator;
             },
             array())

// Get all numeric IDs
array_reduce($inputs,
             function($accumulator, $element) {
               $id = get_id($element);
               if (is_numeric($id)) $accumulator[] = $id;
               return $accumulator;
             },
             array())

注意:我对上述array_map和array_filter的实现不会完全像PHP一样,因为我的array_map一次只能处理一个数组,而我的array_filter不会使用“空”作为其默认$function。此外,两者都不会保留密钥。

让它们表现得像PHP一样并不难,但我觉得这些复杂性会使核心思想更难被发现。