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.

Gras Double
fonte
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
)
Artefacto
fonte
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.

Kendall Hopkins
fonte
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

Steven Schlansker
fonte
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.

Warbo
fonte
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 ).

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