array_map, array_walk和array_filter的区别

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

它们执行相同的事情吗?< br / > 它们可以互换使用吗?< / p >

如果它们完全不同,我将非常感谢您提供举例说明。

200924 次浏览
    <李>改变价值观:
    • array_map不能改变输入数组中的值,而array_walk可以;特别地,array_map从不改变它的参数。
    • 李< / ul > < / >
    • 数组键访问:
      • array_map不能操作数组键,array_walk可以。
      • 李< / ul > < / > <李>返回值:
        • array_map返回一个新数组,array_walk只返回true。因此,如果你不想因为遍历一个数组而创建一个数组,你应该使用array_walk
        • 李< / ul > < / >
        • 迭代多个数组:
          • array_map也可以接收任意数量的数组,并且可以并行遍历它们,而array_walk只对一个数组操作。
          • 李< / ul > < / >
          • 向Callback传递任意数据:
            • array_walk可以接收一个额外的任意参数来传递给回调函数。自PHP 5.3(引入匿名函数时)以来,这几乎无关紧要。
            • 李< / ul > < / >
            • 返回数组长度
              • array_map的结果数组与最大输入数组的长度相同;array_walk不返回数组,但同时它不能改变原始数组的元素数量;array_filter根据过滤函数只选择数组元素的一个子集。它确实保存了钥匙。
              • 李< / ul > < / >

              例子:

              <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
              )
              

从文档来看,

Bool array_walk(数组&$array, callback $funcname [, mixed $userdata]

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

数组array_map(回调$callback, 数组$arr1[,数组$…)<-return array

做了与除了完全相同的事情,它不是就地修改,而是返回一个包含已转换元素的新数组。

数组array_filter(数组$input [, -返回数组

使用F函数array_filter,而不是转换元素,将删除任何F(x)不为真的元素

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

理论上,像函数映射这样的事情可以并行完成,因为应用到数据上的函数应该只影响数据而不影响全局状态。这是因为array_map可以选择将函数应用于中的项的任何顺序(尽管在PHP中不是这样)。

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

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

array_filter实际上是array_walk(或array_reduce)的一个应用,它或多或少只是为了方便而提供的。

下面的修订试图更清楚地描述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中过滤掉,只过滤出水果类型。

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()调用时,这两个参数将自动获取一个元素的值和它的键,并且也是按这个顺序。(参见更多在这里)。

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

array_reduce函数接受一个数组、一个双实参函数和一个'accumulator',如下所示:

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;
};

如您所见,两个函数都接受$累加器并再次返回它。这些功能有两个不同之处:

  • $MAP总是附加到$累加器,但$FILTER只在$function($element)为TRUE时才这样做。
  • $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很大,它的效率会非常低,因为第一次调用(map或filter)将遍历$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不会使用“;empty"作为默认的$函数。另外,这两种方法都不会保存密钥。

使它们像PHP一样运行并不难,但我觉得这些复杂的操作会使核心思想更难被发现。