ChatGPT解决这个技术问题 Extra ChatGPT

array_map、array_walk 和 array_filter 的区别

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

他们做同样的事情吗?它们可以互换使用吗?

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

这是通过 array_reduce() 进行命名数组处理的一个很酷的技巧。如果您正在研究 array_map、array_walk 和 array_filter,值得一读。 stackoverflow.com/questions/11563119/…

M
Martin

更改值:array_map 不能更改输入数组中的值,而 array_walk 可以;特别是,array_map 永远不会改变它的参数。

array_map 不能更改输入数组中的值,而 array_walk 可以;特别是,array_map 永远不会改变它的参数。

数组键访问:array_map 不能用数组键操作,array_walk 可以。

array_map 不能用数组键操作,array_walk 可以。

返回值:array_map 返回一个新数组,array_walk 只返回 true。因此,如果您不想因为遍历一个数组而创建一个数组,您应该使用 array_walk。

array_map 返回一个新数组,array_walk 只返回 true。因此,如果您不想因为遍历一个数组而创建一个数组,您应该使用 array_walk。

迭代多个数组:array_map 也可以接收任意数量的数组,并且可以并行迭代它们,而 array_walk 只对一个数组进行操作。

array_map 也可以接收任意数量的数组,并且可以并行迭代它们,而 array_walk 只对一个进行操作。

将任意数据传递给回调:array_walk 可以接收额外的任意参数以传递给回调。自 PHP 5.3(引入匿名函数时)以来,这几乎无关紧要。

array_walk 可以接收额外的任意参数以传递给回调。自 PHP 5.3(引入匿名函数时)以来,这几乎无关紧要。

返回数组的长度:array_map的结果数组与最大输入数组的长度相同; array_walk 不返回数组但同时不能改变原始数组的元素个数; array_filter 根据过滤函数仅选择数组元素的子集。它确实保留了密钥。

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
)

PHP手册说:“array_walk():只有数组的值可能会被改变;”
“array_map 无法使用数组键操作”这是不正确的:array_map(callback($key, $value), array_keys($array), $array)
它仍然没有访问任何数组的键,它正在访问您放入从键创建的数组中的值。这是一种解决方法,它不会否定该声明。
虽然 array_map 不会隐式更改值,但通过将结果分配给同一个数组,它基本上会更改它,并且对同一数组本身进行操作的“自相矛盾的”array_walk 不会直接更改其值,除非通过引用传递值(数组walk 可能会通过传递原始数组的匿名函数 use 子句间接将索引/元素删除为 array_filter,但这是一种解决方法)。因此,总而言之,更改值,或者是否通过引用返回或传递值的差异较小,但是数组遍历与索引和数组映射一起使用多个数组
此外看起来无论数组遍历将第一个数组参数作为参考,当一个人想要更改它时,他还必须传递回调项值作为参考
N
Nicholas Shanks

mapping 数据数组的函数的想法来自函数式编程。您不应将 array_map 视为对数组的每个元素调用函数的 foreach 循环(即使它是这样实现的)。应该将其视为将函数独立地应用于数组中的每个元素。

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

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

回到 PHP 领域,array_maparray_walk 几乎相同,除了 array_walk 让您可以更好地控制数据的迭代,并且通常用于就地“更改”数据而不是返回一个新的“更改”数组。

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


为您的第 2 段见解 +1 “理论上,函数映射之类的事情可以并行完成,因为应用于数据的函数应该只影响数据而不是全局状态。”对于我们并行程序员来说,记住这一点很有用。
您能解释一下如何使用 array_walk() 实现 array_filter()
n
newbie

从文档中,

bool array_walk ( 数组 &$array , 回调 $funcname [, 混合 $userdata ] ) <-return bool

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

数组 array_map ( 回调 $callback , 数组 $arr1 [, 数组 $... ] )<-return 数组

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

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

array_filter 与函数 F,而不是转换元素,将删除 F(x) 不正确的任何元素


无法弄清楚为什么我的数组值消失了。查看文档,我假设 array_walk 返回了一个类似 array_map 的数组,并认为问题出在我的函数中。直到我看到返回类型是布尔值时才意识到。
l
liamvictor

其他答案很好地证明了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,但 $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 不会使用“空”作为其默认的$ 函数。此外,两者都不会保留密钥。

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


s
slevy1

以下修订旨在更清楚地描述 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 中过滤掉,只留下 $filtered 的水果类型。

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

<?php

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

实时代码 here

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

在下一个片段中,数组 walk() 遍历 $nu 并针对引用运算符 '&' 对每个元素进行更改。无需创建额外的数组即可进行更改。每个元素的值都在适当的位置更改为一个信息量更大的字符串,指定其键、类别和值。

<?php

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

请参阅demo

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


请注意,函数 $lambda$callback 只是现有函数的 eta 扩展,因此完全是多余的。您可以通过传递底层函数(名称)来获得相同的结果:$filtered = array_filter($array, 'ctype_alpha');$nu = array_map('strtoupper', $filtered);