web-dev-qa-db-ja.com

array_map、array_walk、およびarray_filterの違い

array_maparray_walkおよびarray_filterの違いはまさにその違いです。私がドキュメンテーションから見ることができたのはあなたが供給された配列で動作を実行するためにコールバック関数を渡すことができるということです。しかし、私はそれらの間に特別な違いを見つけるようには思われません。

彼らは同じことをしますか?
それらは互換的に使用できますか?

それらがまったく異なっているならば、私は実例とのあなたの助けに感謝します。

351
Web Logic
  • 値を変更する:
    • array_map は入力配列内の値を変更できませんが、 array_walk は変更できます。特に array_map は引数を変更しません。
  • 配列キーアクセス:
  • 戻り値:
    • array_map は新しい配列を返します。 array_walktrueのみを返します。したがって、1つの配列をトラバースした結果として配列を作成したくない場合は、 array_walk を使用してください。
  • 複数の配列を繰り返す:
    • array_map は任意の数の配列を受け取ることができ、それらを並列に繰り返すことができます。一方、 array_walk は1つのみで動作します。
  • 任意のデータをコールバックに渡す:
    • array_walk はコールバックに渡すために追加の任意のパラメータを受け取ることができます。これはPHP 5.3からほとんど関係がありません( 無名関数 が導入されたとき)。
  • 返される配列の長さ:
    • 結果の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
)
524
Artefacto

マッピング データの配列への関数という考えは、関数型プログラミングから来ています。 array_mapを配列の各要素の関数を呼び出すforeachループと考えるべきではありません(それがどのように実装されているとしても)。関数を配列の各要素に独立して適用すると考えるべきです。

理論的には、データに適用される関数はグローバル状態ではなくデータにのみ影響を与えるため、関数マッピングなどのことは並行して実行できます。これは、array_mapが(PHPではそうではないにしても)アイテムに関数を適用する順序を選択できるためです。

一方、array_walkは、データの配列を処理する場合とは正反対の方法です。各項目を個別に処理する代わりに、状態(&$userdata)を使用して、項目を適切に編集することができます(foreachループのように)。アイテムに$funcnameが適用されるたびに、プログラムのグローバル状態が変わる可能性があるため、アイテムを処理する単一の正しい方法が必要です。 。

PHPの話に戻ると、array_maparray_walkは、array_walkがデータの繰り返しをより細かく制御できることと、通常はインプレースデータを「変更する」ことと、新しい「変更された」配列を返すことを除いてほぼ同じです。

array_filterは、実際にはarray_walk(またはarray_reduce)のアプリケーションであり、多かれ少なかれ便宜上提供されています。

88
Kendall Hopkins

ドキュメンテーションから、

bool array_walk(array&$ array、コールバック$ funcname [、mixed $ userdata])<-return bool

array_walk は配列と関数Fを取り、すべての要素xをF(x)に置き換えることによってそれを修正します。

配列array_map(コールバック$ callback、配列$ arr1 [、配列$ ...])< - 配列を返す

array_map は全く同じことをしますexceptその場で修正する代わりに、変換された要素を持つ新しい配列を返します。

array_filter(array $ input [、callback $ callback])< - 配列を返す

array_filter 関数Fを使用すると、要素を変換する代わりに、F(x)が真でない要素はすべて削除されます。

39

他の答えは、array_walk(インプレース変更)とarray_map(変更されたコピーを返す)の違いを非常によく示しています。しかし、彼らは本当にarray_reduceについて言及していません。これは、array_mapとarray_filterを理解するための明らかな方法です。

Array_reduce関数は、次のように、配列、引数が2つの関数、および「アキュムレータ」を取ります。

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

指定された関数を使用して、配列の要素が1つずつアキュムレータと結合されます。上記の呼び出しの結果は、これを行うのと同じです。

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

このループバージョンは、なぜ3番目の引数を「アキュムレータ」と呼んでいるのかを明確にしています。それを使用して、各反復を通じて結果を累積することができます。

それでこれは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のもう1つの変わったことです。重要な点は、右側が私が$ MAPと$ FILTERと呼んだ関数を除いて同一であるということです。それで、彼らはどのように見えますか?

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

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

お分かりのように、両方の関数は$ accumulatorを受け取り、それを再び返します。これらの機能には2つの違いがあります。

  • $ MAPは常に$ accumulatorに追加されますが、$ FILTERは$ function($ element)がTRUEの場合にのみ追加されます。
  • $ FILTERは元の要素を追加しますが、$ MAPは$ function($ element)を追加します。

これは無駄なトリビアとはほど遠いことに注意してください。アルゴリズムをより効率的にするためにそれを使うことができます。

次の2つの例のようなコードがよく見られます。

// 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を使用すると、これらの例はとても素敵に見えます。ただし、最初の呼び出し(mapまたはfilter)は$ inputをトラバースして中間配列を構築するため、$ inputが大きいと非常に非効率的になる可能性があります。この中間配列は2番目の呼び出しに直接渡されます。これにより、全体が再びトラバースされ、中間配列はガベージコレクションされる必要があります。

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はデフォルトの$関数として "empty"を使わないので、上記のarray_mapとarray_filterの実装はPHPのようには振舞いません。また、どちらもキーを保存しません。

それらをPHPのように動作させることは難しくありませんが、これらの複雑さがコアのアイデアを見つけにくくすると私は感じました。

19
Warbo

次の改訂は、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()とは異なり、次のように各要素にコールバックを適用するため、結果の配列にはフィルタリングされたが変更された値のevery要素が含まれます。

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

demo をご覧ください。

注意:array_walk()に関するコールバック関数は、array_walk()によって呼び出されたときにも、要素の値とそのキーをその順番で自動的に取得する2つのパラメータを取ります。 (もっと見る ここ ).

1
slevy1