Perbedaan antara array_map, array_walk dan array_filter

373

Apa sebenarnya perbedaan antara array_map, array_walkdan array_filter. Apa yang bisa saya lihat dari dokumentasi adalah bahwa Anda dapat melewati fungsi panggilan balik untuk melakukan suatu tindakan pada array yang disediakan. Tapi sepertinya saya tidak menemukan perbedaan khusus di antara mereka.

Apakah mereka melakukan hal yang sama?
Bisakah mereka digunakan secara bergantian?

Saya akan menghargai bantuan Anda dengan contoh ilustratif jika mereka berbeda sama sekali.

Gras Double
sumber
Ini adalah trik keren untuk pemrosesan array bernama melalui array_reduce (). Layak dibaca jika Anda sedang menyelidiki array_map, array_walk, dan array_filter. stackoverflow.com/questions/11563119/…
Lance Cleveland

Jawaban:

564
  • Mengubah Nilai:
  • Akses Kunci Array:
  • Nilai Pengembalian:
    • array_mapmengembalikan array baru, array_walkhanya mengembalikan true. Oleh karena itu, jika Anda tidak ingin membuat array sebagai akibat dari melintasi satu array, Anda harus menggunakan array_walk.
  • Iterasi Beberapa Array:
    • array_mapjuga dapat menerima jumlah array yang sewenang-wenang dan dapat beralih di atasnya secara paralel, sementara array_walkhanya beroperasi pada satu array .
  • Melewati Data Sewenang-wenang ke Callback:
    • array_walkdapat menerima parameter arbitrer tambahan untuk diteruskan ke panggilan balik. Ini sebagian besar tidak relevan sejak PHP 5.3 (ketika fungsi anonim diperkenalkan).
  • Panjang Array yang Dikembalikan:
    • Array yang dihasilkan array_mapmemiliki panjang yang sama dengan array input terbesar; array_walktidak mengembalikan array tetapi pada saat yang sama tidak dapat mengubah jumlah elemen array asli; array_filterhanya mengambil subset elemen-elemen array sesuai dengan fungsi penyaringan. Itu mempertahankan kunci.

Contoh:

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

Hasil:

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
sumber
3
Manual PHP mengatakan: "array_walk (): Hanya nilai-nilai array yang berpotensi diubah;"
feeela
10
"array_map tidak dapat beroperasi dengan kunci array" ini tidak benar:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski
12
Itu masih tidak mengakses kunci dari array apa pun, itu mengakses nilai yang Anda masukkan ke dalam array yang Anda buat dari kunci. Ini solusinya, itu tidak meniadakan pernyataan itu.
inarilo
sementara array_map tidak secara implisit mengubah nilai-nilai, dengan menetapkan hasil ke array yang sama pada dasarnya mengubahnya, dan secara paradoks 'array_walk yang beroperasi pada array yang sama itu sendiri tidak akan mengubah nilainya secara langsung, kecuali nilai yang diteruskan oleh referensi (array walk mungkin menghapus indeks / elemen sebagai array_filter secara tidak langsung melalui fungsi anonim menggunakan klausa yang melewati array asli tetapi ini solusinya). Untuk menyimpulkan demikian, mengubah nilai, atau jika nilai dikembalikan atau dilewatkan dengan referensi kurang berbeda secara efektif, tetapi array walk bekerja dengan indeks dan peta array dengan banyak array
FantomX1
lebih jauh lagi terlihat seperti itu tidak peduli bahwa array walk mengambil yang pertama, parameter array sebagai referensi, ketika seseorang ingin mengubahnya dia harus lulus juga nilai item callback sebagai referensi
FantomX1
91

Gagasan pemetaan fungsi ke array data berasal dari pemrograman fungsional. Anda seharusnya tidak memikirkannya array_mapsebagai foreachloop yang memanggil fungsi pada setiap elemen array (meskipun begitulah penerapannya). Itu harus dianggap sebagai menerapkan fungsi ke setiap elemen dalam array secara mandiri.

Secara teori hal-hal seperti pemetaan fungsi dapat dilakukan secara paralel karena fungsi yang diterapkan pada data HANYA memengaruhi data dan BUKAN keadaan global. Ini karena seorang array_mapdapat memilih urutan apa pun untuk menerapkan fungsi ke item dalam (meskipun dalam PHP tidak).

array_walkdi sisi lain itu pendekatan yang berlawanan untuk menangani array data. Alih-alih menangani setiap item secara terpisah, ia menggunakan status ( &$userdata) dan dapat mengedit item di tempatnya (seperti loop foreach). Karena setiap kali item $funcnamemenerapkannya, item tersebut dapat mengubah status global program dan karenanya memerlukan cara tunggal yang benar dalam memproses item.

Kembali di tanah PHP, array_mapdan array_walkhampir identik kecuali array_walkmemberi Anda lebih banyak kontrol atas iterasi data dan biasanya digunakan untuk "mengubah" data di tempat vs mengembalikan array "diubah" yang baru.

array_filterbenar-benar sebuah aplikasi dari array_walk(atau array_reduce) dan lebih-kurang hanya disediakan untuk kenyamanan.

Kendall Hopkins
sumber
5
+1 untuk paragraf ke-2 Anda wawasan "Secara teori hal-hal seperti pemetaan fungsi dapat dilakukan secara paralel karena fungsi yang diterapkan pada data HANYA mempengaruhi data dan BUKAN keadaan global." Bagi kami programmer paralel, itu hal yang berguna untuk diingat.
eter
Bisakah Anda menjelaskan bagaimana array_filter()bisa diterapkan menggunakan array_walk()?
pfrenssen
40

Dari dokumentasi,

bool array_walk (array & $ array, callback $ funcname [, mixed $ userdata]) <-kembali bool

array_walk mengambil array dan fungsi Fdan memodifikasinya dengan mengganti setiap elemen x dengan F(x).

array array_map (callback $ callback, array $ arr1 [, array $ ...]) <- return array

array_map melakukan hal yang sama persis kecuali bahwa alih-alih memodifikasi di tempat itu akan mengembalikan array baru dengan elemen yang diubah.

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

array_filter dengan fungsi F, alih-alih mengubah elemen, akan menghapus elemen apa pun yang F(x)tidak benar

Steven Schlansker
sumber
Tidak dapat mengetahui mengapa nilai array saya menghilang. Melihat dokumentasi saya berasumsi array_walkkembali array seperti array_mapdan menemukan masalah itu dalam fungsi saya. Tidak menyadari sampai saya melihat ini bahwa tipe kembalinya adalah boolean.
Dylan Valade
22

Jawaban lain menunjukkan perbedaan antara array_walk (modifikasi di tempat) dan array_map (kembalikan salinan yang dimodifikasi) dengan cukup baik. Namun, mereka tidak benar-benar menyebutkan array_reduce, yang merupakan cara menerangi untuk memahami array_map dan array_filter.

Fungsi array_reduce mengambil array, fungsi dua argumen dan 'akumulator', seperti ini:

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

Elemen array dikombinasikan dengan akumulator satu per satu, menggunakan fungsi yang diberikan. Hasil dari panggilan di atas sama dengan melakukan ini:

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

Jika Anda lebih suka berpikir dalam hal loop, itu seperti melakukan yang berikut (Saya sebenarnya menggunakan ini sebagai fallback ketika array_reduce tidak tersedia):

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

Versi perulangan ini menjelaskan mengapa saya menyebut argumen ketiga sebagai 'akumulator': kita dapat menggunakannya untuk mengumpulkan hasil melalui setiap iterasi.

Jadi apa hubungannya dengan array_map dan array_filter? Ternyata mereka berdua jenis array_reduce tertentu. Kita bisa menerapkannya seperti ini:

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

Abaikan fakta bahwa array_map dan array_filter mengambil argumen mereka dalam urutan yang berbeda; itu hanyalah kekhasan dari PHP. Poin penting adalah bahwa sisi kanan identik kecuali untuk fungsi yang saya sebut $ MAP dan $ FILTER. Jadi, seperti apa mereka?

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

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

Seperti yang Anda lihat, kedua fungsi menerima $ akumulator dan mengembalikannya lagi. Ada dua perbedaan dalam fungsi-fungsi ini:

  • $ MAP akan selalu ditambahkan ke $ akumulator, tetapi $ FILTER hanya akan melakukannya jika $ function ($ element) BENAR.
  • $ FILTER menambahkan elemen asli, tetapi $ MAP menambahkan $ function ($ element).

Perhatikan bahwa ini jauh dari hal-hal sepele yang tidak berguna; kita dapat menggunakannya untuk membuat algoritma kita lebih efisien!

Kita sering dapat melihat kode seperti dua contoh ini:

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

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

Menggunakan array_map dan array_filter bukan loop membuat contoh-contoh ini terlihat cukup bagus. Namun, bisa jadi sangat tidak efisien jika $ input besar, karena panggilan pertama (peta atau filter) akan melintasi $ input dan membangun array perantara. Array perantara ini diteruskan langsung ke panggilan kedua, yang akan melintasi semuanya lagi, maka array perantara perlu dikumpulkan dari sampah.

Kita dapat menyingkirkan array perantara ini dengan mengeksploitasi fakta bahwa array_map dan array_filter adalah contoh dari array_reduce. Dengan menggabungkan mereka, kita hanya perlu menelusuri $ input sekali dalam setiap contoh:

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

CATATAN: Implementasi saya array_map dan array_filter di atas tidak akan berperilaku persis seperti PHP, karena array_map saya hanya dapat menangani satu array pada suatu waktu dan array_filter saya tidak akan menggunakan "kosong" sebagai fungsi $ default. Juga, tidak ada yang akan menyimpan kunci.

Tidak sulit untuk membuat mereka berperilaku seperti PHP, tetapi saya merasa bahwa komplikasi ini akan membuat ide inti lebih sulit dikenali.

Warbo
sumber
1

Revisi berikut berusaha untuk menggambarkan dengan lebih jelas PHP's array_filer (), array_map (), dan array_walk (), yang semuanya berasal dari pemrograman fungsional:

array_filter () memfilter data, menghasilkan array baru yang hanya menahan item yang diinginkan dari array sebelumnya, sebagai berikut:

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

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

kode langsung di sini

Semua nilai numerik disaring dari $ array, meninggalkan $ difilter dengan hanya jenis buah.

array_map () juga membuat array baru tetapi tidak seperti array_filter () array yang dihasilkan berisi setiap elemen dari input $ filter tetapi dengan nilai yang diubah, karena menerapkan callback ke setiap elemen, sebagai berikut:

<?php

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

kode langsung di sini

Kode dalam hal ini berlaku panggilan balik menggunakan strtoupper built-in () tetapi fungsi yang ditentukan pengguna juga merupakan pilihan lain yang layak. Callback berlaku untuk setiap item $ filtered dan karenanya menghasilkan $ nu yang elemennya mengandung nilai huruf besar.

Dalam cuplikan berikutnya, array walk () melintasi $ nu dan membuat perubahan pada setiap elemen vis a vis operator referensi '&'. Perubahan terjadi tanpa membuat array tambahan. Nilai setiap elemen berubah di tempatnya menjadi string yang lebih informatif yang menentukan kunci, kategori, dan nilainya.

<?php

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

Lihat demo

Catatan: fungsi callback sehubungan dengan array_walk () mengambil dua parameter yang secara otomatis akan memperoleh nilai elemen dan kunci dan dalam urutan itu, juga ketika dipanggil oleh array_walk (). (Lihat lebih lanjut di sini ).

slevy1
sumber
1
Perhatikan bahwa fungsi $lambdadan $callbackhanya ekspansi eta dari fungsi yang ada, dan karenanya sepenuhnya berlebihan. Anda bisa mendapatkan hasil yang sama dengan melewati (nama) fungsi yang mendasarinya: $filtered = array_filter($array, 'ctype_alpha');dan$nu = array_map('strtoupper', $filtered);
Warbo