array_map,array_walk和array_filter之间的区别

时间:2010-08-07 22:19:36

标签: php arrays callback

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

他们是否表现相同?
它们可以互换使用吗?

如果它们完全不同,我将非常感谢您对说明性示例的帮助。

5 个答案:

答案 0 :(得分:522)

  • 改变价值观:
  • 数组键访问:
  • 返回值:
    • array_map返回一个新数组,array_walk仅返回true / false。因此,如果您不想通过遍历一个数组来创建数组,则应使用array_walk
  • 迭代多个数组:
    • array_map也可以接收任意数量的数组,并且可以并行迭代它们,而array_walk只能在一个数组上运行。
  • 将任意数据传递给回调:
  • 返回数组的长度:
    • 得到的array_map数组与最大输入数组的长度相同; array_walk不返回数组,但同时它不能改变原始数组的元素数量; array_filter根据过滤函数仅选择数组元素的子集。它确实保留了密钥。

示例:

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

答案 1 :(得分:88)

mapping数据数组函数的概念来自函数式编程。您不应该将array_map视为一个foreach循环,它在数组的每个元素上调用一个函数(即使它是如何实现的)。它应该被认为是将函数独立地应用于数组中的每个元素。

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

另一方面,

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

回到PHP版本,array_maparray_walk几乎完全相同,除了array_walk让您可以更好地控制数据的迭代,通常用于“更改”数据到位vs返回一个新的“已更改”数组。

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

答案 2 :(得分:39)

从文档中

  

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

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

  

array array_map(callback $ callback,   array $ arr1 [,array $ ...])&lt; -return array

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

  

array array_filter(array $ input [,   callback $ callback])&lt; -return array

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

答案 3 :(得分:19)

其他答案很好地证明了array_walk(就地修改)和array_map(返回修改后的副本)之间的区别。但是,他们并没有真正提到array_reduce,这是了解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;
};

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

  • $ MAP将始终附加到$ accumulator,但$ 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而不是循环使这些示例看起来非常好。但是,如果$ input很大,则效率非常低,因为第一次调用(map或filter)将遍历$ input并构建一个中间数组。这个中间数组直接传递给第二个调用,它将遍历整个事件,然后中间数组将需要进行垃圾回收。

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

// 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”作为默认的$ function。此外,两者都不会保留密钥。

让它们表现得像PHP一样并不困难,但我觉得这些并发症会让核心想法变得难以发现。

答案 4 :(得分:1)

以下修订旨在更清楚地描述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);
?>

实时代码here

所有数值都是从$ array中过滤掉的,只过滤了$仅过滤水果。

array_map()也创建了一个新数组,但与array_filter()不同,结果数组包含输入$ filter的每个元素,但由于对每个元素应用了一个回调,因此具有更改的值,如下所示:

<?php

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

实时代码here

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

在下一个片段中,数组walk()遍历$ nu并对参考运算符'&amp;'进行每个元素的更改。无需创建其他阵列即可进行更改。每个元素的值都会更改为更具信息性的字符串,指定其键,类别和值。

<?php

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

请参阅demo

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

相关问题