Diferença entre array_map, array_walk e array_filter


373

Qual é exatamente a diferença entre array_map, array_walke array_filter. O que pude ver na documentação é que você pode transmitir uma função de retorno de chamada para executar uma ação na matriz fornecida. Mas não pareço encontrar nenhuma diferença em particular entre eles.

Eles executam a mesma coisa?
Eles podem ser usados ​​de forma intercambiável?

Agradeço sua ajuda com exemplos ilustrativos, caso sejam diferentes.


Esse é um truque legal para o processamento de matriz nomeada via array_reduce (). Vale a pena ler se você estiver investigando array_map, array_walk e array_filter. stackoverflow.com/questions/11563119/…
Lance Cleveland

Respostas:


564
  • Alteração de valores:
    • array_mapnão pode alterar os valores dentro da (s) matriz (s) de entrada enquanto array_walkpode; em particular, array_mapnunca muda seus argumentos.
  • Acesso às chaves da matriz:
  • Valor de retorno:
    • array_mapretorna uma nova matriz, array_walksomente retorna true. Portanto, se você não deseja criar uma matriz como resultado de atravessar uma matriz, deve usar array_walk.
  • Iterando várias matrizes:
    • array_maptambém pode receber um número arbitrário de matrizes e pode iterar sobre elas em paralelo, enquanto array_walkopera apenas em uma.
  • Passagem de dados arbitrários para retorno de chamada:
    • array_walkpode receber um parâmetro arbitrário extra para passar para o retorno de chamada. Isso é irrelevante desde o PHP 5.3 (quando funções anônimas foram introduzidas).
  • Comprimento da matriz retornada:
    • A matriz resultante array_maptem o mesmo comprimento que a da maior matriz de entrada; array_walknão retorna uma matriz, mas ao mesmo tempo não pode alterar o número de elementos da matriz original; array_filterseleciona apenas um subconjunto dos elementos da matriz de acordo com uma função de filtragem. Ele preserva as chaves.

Exemplo:

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

Resultado:

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
)

3
O manual do PHP diz: "array_walk (): apenas os valores da matriz podem ser potencialmente alterados;"
Fee26

10
"array_map não pode operar com as chaves do array" Isso não é verdade:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski

12
Ainda não está acessando as chaves de nenhuma matriz, está acessando os valores que você coloca em uma matriz que você criou a partir das chaves. É uma solução alternativa, não nega a afirmação.
Inarilo

enquanto array_map não altera implicitamente os valores, atribuindo o resultado à mesma matriz, basicamente o altera, e 'paradoxalmente' array_walk que opera no mesmo array em si não altera seus valores diretamente, a menos que o valor passado por referência (array walk pode remover índices / elementos como array_filter indiretamente, através da cláusula use anonymous function, passando a matriz original, mas é uma solução alternativa). Para concluir, portanto, mudança de valores, nem se um valor for devolvido ou passados por referência é de menos diferença de forma eficaz, mas gama caminhada trabalha com índices e mapa matriz com múltiplas matrizes
FantomX1

Além disso parece que não importa o que variedade passeio leva o primeiro, parâmetro de matriz como uma referência, quando se quer mudar isso ele tem de passar também o valor do item de retorno de chamada como uma referência
FantomX1

91

A idéia de mapear uma função para a matriz de dados vem da programação funcional. Você não deve pensar em array_mapum foreachloop que chama uma função em cada elemento da matriz (mesmo que seja assim que é implementado). Deve-se considerar a aplicação da função a cada elemento da matriz independentemente.

Em teoria, coisas como mapeamento de funções podem ser feitas em paralelo, pois a função aplicada aos dados deve afetar APENAS os dados e NÃO o estado global. Isso ocorre porque é array_mappossível escolher qualquer ordem na qual aplicar a função aos itens (embora isso não ocorra no PHP).

array_walkpor outro lado, é a abordagem exatamente oposta ao tratamento de matrizes de dados. Em vez de manipular cada item separadamente, ele usa um estado ( &$userdata) e pode editar o item no local (como um loop foreach). Como cada vez que um item é $funcnameaplicado a ele, ele pode alterar o estado global do programa e, portanto, requer uma única maneira correta de processar os itens.

De volta ao PHP, array_mape array_walksão quase idênticos, exceto array_walkdão a você mais controle sobre a iteração de dados e são normalmente usados ​​para "alterar" os dados no local versus retornar um novo array "alterado".

array_filteré realmente uma aplicação de array_walk(ou array_reduce) e é mais ou menos fornecida apenas por conveniência.


5
+1 para obter uma visão do segundo parágrafo de "Em teoria, coisas como mapeamento de funções podem ser feitas em paralelo, pois a função aplicada aos dados deve afetar SOMENTE os dados e NÃO o estado global". Para nós, programadores paralelos, é uma coisa útil a ter em mente.
Etherice

Você pode explicar como array_filter()pode ser implementado usando array_walk()?
Pfrenssen

40

A partir da documentação,

bool array_walk (array & $ array, retorno de chamada $ funcname [, misto de $ userdata]) <-return bool

array_walk pega uma matriz e uma função Fe a modifica substituindo cada elemento x por F(x).

array array_map (retorno de chamada $ retorno de chamada, matriz $ arr1 [, matriz $ ...]) <- retornar matriz

array_map faz exatamente a mesma coisa, exceto que, em vez de modificar no local, ele retornará uma nova matriz com os elementos transformados.

array array_filter (array $ input [, callback $ callback]) <- retorna o array

array_filter com função F, em vez de transformar os elementos, removerá qualquer elemento que F(x)não seja verdadeiro


Não foi possível descobrir por que meus valores de matriz desapareceram. Analisando a documentação, presumi que array_walkretornasse uma matriz array_mape imaginei que o problema estava em minha função. Não percebi até que vi que o tipo de retorno é booleano.
Dylan Valade

22

As outras respostas demonstram a diferença entre array_walk (modificação no local) e array_map (retornar cópia modificada) muito bem. No entanto, eles realmente não mencionam array_reduce, que é uma maneira esclarecedora de entender array_map e array_filter.

A função array_reduce pega uma matriz, uma função de dois argumentos e um 'acumulador', assim:

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

Os elementos da matriz são combinados com o acumulador, um de cada vez, usando a função fornecida. O resultado da chamada acima é o mesmo que fazer isso:

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

Se você preferir pensar em termos de loops, é como fazer o seguinte (na verdade, usei isso como um fallback quando array_reduce não estava disponível):

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

Esta versão em loop deixa claro por que chamei o terceiro argumento de 'acumulador': podemos usá-lo para acumular resultados a cada iteração.

Então, o que isso tem a ver com array_map e array_filter? Acontece que ambos são um tipo específico de array_reduce. Podemos implementá-los assim:

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

Ignore o fato de que array_map e array_filter recebem seus argumentos em uma ordem diferente; isso é apenas outra peculiaridade do PHP. O ponto importante é que o lado direito é idêntico, exceto pelas funções que chamei de $ MAP e $ FILTER. Então, como eles são?

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

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

Como você pode ver, ambas as funções pegam o acumulador $ e o devolvem novamente. Existem duas diferenças nessas funções:

  • O $ MAP sempre será anexado ao acumulador $, mas o $ FILTER somente o fará se a função $ (elemento $) for VERDADEIRA.
  • $ FILTER anexa o elemento original, mas $ MAP acrescenta a função $ (elemento $).

Observe que isso está longe de ser trivial; podemos usá-lo para tornar nossos algoritmos mais eficientes!

Muitas vezes, podemos ver código como esses dois exemplos:

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

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

Usar array_map e array_filter em vez de loops faz com que esses exemplos pareçam bastante agradáveis. No entanto, pode ser muito ineficiente se $ inputs for grande, pois a primeira chamada (mapa ou filtro) percorrerá $ inputs e criará uma matriz intermediária. Essa matriz intermediária é passada diretamente para a segunda chamada, que percorrerá a coisa toda novamente, e a matriz intermediária precisará ser coletada como lixo.

Podemos nos livrar dessa matriz intermediária explorando o fato de que array_map e array_filter são exemplos de array_reduce. Ao combiná-los, precisamos apenas percorrer $ inputs uma vez em cada exemplo:

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

NOTA: Minhas implementações de array_map e array_filter acima não se comportarão exatamente como as do PHP, pois meu array_map pode lidar apenas com uma matriz de cada vez e meu array_filter não usará "vazio" como sua função $ padrão. Além disso, nenhum dos dois preservará as chaves.

Não é difícil fazê-los se comportar como os do PHP, mas senti que essas complicações dificultariam a identificação da ideia principal.


1

A revisão a seguir procura delinear com mais clareza array_filer (), array_map () e array_walk () do PHP, todos originários da programação funcional:

array_filter () filtra os dados, produzindo como resultado uma nova matriz contendo apenas os itens desejados da matriz anterior, da seguinte maneira:

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

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

código ao vivo aqui

Todos os valores numéricos são filtrados de $ array, deixando $ filtrado apenas com tipos de frutas.

array_map () também cria uma nova matriz, mas, diferentemente de array_filter (), a matriz resultante contém todos os elementos da entrada $ filtrados, mas com valores alterados, devido à aplicação de um retorno de chamada para cada elemento, da seguinte maneira:

<?php

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

código ao vivo aqui

O código nesse caso aplica um retorno de chamada usando o strtoupper () interno, mas uma função definida pelo usuário também é outra opção viável. O retorno de chamada se aplica a todos os itens de $ filtrados e, assim, gera $ nu cujos elementos contêm valores em maiúsculas.

No próximo trecho, o array walk () percorre $ nu e faz alterações em cada elemento em relação ao operador de referência '&'. As alterações ocorrem sem criar uma matriz adicional. O valor de cada elemento é alterado para uma sequência mais informativa, especificando sua chave, categoria e valor.

<?php

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

Ver demonstração

Nota: a função de retorno de chamada em relação a array_walk () usa dois parâmetros que adquirem automaticamente o valor de um elemento e sua chave e nessa ordem, também quando são invocados por array_walk (). (Veja mais aqui ).


11
Observe que as funções $lambdae $callbacksão apenas etaexpansões de funções existentes e, portanto, são completamente redundantes. Você pode obter o mesmo resultado passando (o nome da) a função subjacente: $filtered = array_filter($array, 'ctype_alpha');e$nu = array_map('strtoupper', $filtered);
Warbo
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.