Sự khác biệt giữa mảng_map, mảng_walk và mảng_filter


373

Chính xác thì sự khác biệt giữa array_map, array_walkarray_filter. Những gì tôi có thể thấy từ tài liệu là bạn có thể truyền hàm gọi lại để thực hiện một hành động trên mảng được cung cấp. Nhưng tôi dường như không tìm thấy bất kỳ sự khác biệt cụ thể giữa chúng.

Họ có thực hiện điều tương tự?
Chúng có thể được sử dụng thay thế cho nhau?

Tôi sẽ đánh giá cao sự giúp đỡ của bạn với ví dụ minh họa nếu chúng khác nhau.


Đây là một mẹo hay để xử lý mảng có tên thông qua Array_reduce (). Đáng đọc nếu bạn đang nghiên cứu mảng_map, mảng_walk và mảng_filter. stackoverflow.com/questions/11563119/
Lance Cleveland

Câu trả lời:


564
  • Thay đổi giá trị:
    • array_mapkhông thể thay đổi các giá trị bên trong (các) mảng đầu vào trong khi array_walkcó thể; đặc biệt, array_mapkhông bao giờ thay đổi đối số của nó.
  • Truy cập khóa mảng:
  • Giá trị trả về:
    • array_maptrả về một mảng mới, array_walkchỉ trả lại true. Do đó, nếu bạn không muốn tạo một mảng là kết quả của việc duyệt qua một mảng, bạn nên sử dụng array_walk.
  • Lặp lại nhiều mảng:
    • array_mapcũng có thể nhận được một số lượng các mảng tùy ý và nó có thể lặp lại song song với chúng, trong khi array_walkchỉ hoạt động trên một mảng .
  • Truyền dữ liệu tùy ý để gọi lại:
    • array_walkcó thể nhận thêm một tham số tùy ý để chuyển đến cuộc gọi lại. Điều này chủ yếu không liên quan kể từ PHP 5.3 (khi các hàm ẩn danh được giới thiệu).
  • Độ dài của mảng trả về:
    • Mảng kết quả array_mapcó cùng độ dài với mảng đầu vào lớn nhất; array_walkkhông trả về một mảng nhưng đồng thời nó không thể thay đổi số lượng phần tử của mảng ban đầu; array_filterchỉ chọn một tập hợp con của các phần tử của mảng theo chức năng lọc. Nó không bảo quản các phím.

Thí dụ:

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

Kết quả:

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
Hướng dẫn PHP nói: "Array_walk (): Chỉ các giá trị của mảng có thể có khả năng thay đổi;"
feeela

10
"Array_map không thể hoạt động với các khóa mảng" điều này không đúng:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski

12
Nó vẫn không truy cập vào các khóa của bất kỳ mảng nào, nó nhận được các giá trị bạn đặt trong một mảng mà bạn đã tạo từ các khóa. Đó là một cách giải quyết, nó không phủ nhận tuyên bố.
inarilo

trong khi Array_map không thay đổi hoàn toàn các giá trị, bằng cách gán kết quả cho cùng một mảng, về cơ bản nó sẽ thay đổi nó và 'nghịch lý' mảng_walk hoạt động trên cùng một mảng sẽ không thay đổi trực tiếp các giá trị của nó, trừ khi truyền giá trị theo tham chiếu (mảng walk có thể loại bỏ các chỉ mục / phần tử dưới dạng mảng_filter một cách gián tiếp thông qua mệnh đề sử dụng hàm ẩn danh đi qua mảng ban đầu nhưng đó là một cách giải quyết). Do đó, để kết luận, việc thay đổi giá trị, cũng như nếu một giá trị được trả về hoặc chuyển qua tham chiếu ít hiệu quả hơn, nhưng đi bộ mảng hoạt động với các chỉ mục và bản đồ mảng với nhiều mảng
FantomX1

hơn nữa có vẻ như bất kể mảng đi bộ nào lấy tham số mảng đầu tiên làm tham chiếu, khi muốn thay đổi, anh ta cũng phải chuyển giá trị mục gọi lại làm tham chiếu
FantomX1

91

Ý tưởng ánh xạ một hàm vào mảng dữ liệu xuất phát từ lập trình hàm. Bạn không nên nghĩ về array_mapmột foreachvòng lặp gọi một hàm trên từng thành phần của mảng (mặc dù đó là cách nó được triển khai). Nó nên được coi là áp dụng hàm cho từng phần tử trong mảng một cách độc lập.

Về lý thuyết, những thứ như ánh xạ hàm có thể được thực hiện song song vì hàm được áp dụng cho dữ liệu sẽ CHỈ ảnh hưởng đến dữ liệu và KHÔNG phải là trạng thái toàn cục. Điều này là do người array_mapta có thể chọn bất kỳ thứ tự nào để áp dụng hàm cho các mục trong (mặc dù trong PHP thì không).

array_walkmặt khác, cách tiếp cận hoàn toàn ngược lại để xử lý các mảng dữ liệu. Thay vì xử lý từng mục riêng biệt, nó sử dụng trạng thái ( &$userdata) và có thể chỉnh sửa mục tại chỗ (giống như vòng lặp foreach). Vì mỗi lần một mục có các $funcnameáp dụng cho nó, nó có thể thay đổi trạng thái toàn cầu của chương trình và do đòi hỏi một single đúng cách chế biến các mặt hàng.

Quay trở lại vùng đất PHP array_maparray_walkgần như giống hệt nhau ngoại trừ array_walkcho phép bạn kiểm soát nhiều hơn việc lặp lại dữ liệu và thường được sử dụng để "thay đổi" dữ liệu tại chỗ so với trả về một mảng "đã thay đổi" mới.

array_filterthực sự là một ứng dụng của array_walk(hoặc array_reduce) và nó ít nhiều chỉ được cung cấp để thuận tiện.


5
+1 cho cái nhìn sâu sắc về đoạn 2 của bạn về "Về lý thuyết, những thứ như ánh xạ hàm có thể được thực hiện song song vì hàm được áp dụng cho dữ liệu sẽ CHỈ ảnh hưởng đến dữ liệu và KHÔNG phải là trạng thái toàn cầu." Đối với chúng tôi lập trình viên song song, đó là một điều hữu ích cần ghi nhớ.
etherice

Bạn có thể giải thích làm thế nào array_filter()có thể được thực hiện bằng cách sử dụng array_walk()?
pfrenssen

40

Từ tài liệu,

bool Array_walk (mảng & $ mảng, gọi lại $ funcname [, hỗn hợp $ userdata]) <-return bool

Array_walk lấy một mảng và một hàm Fvà sửa đổi nó bằng cách thay thế mọi phần tử x bằng F(x).

mảng Array_map (gọi lại $ gọi lại, mảng $ Array1 [, mảng $ ...]) <- mảng trả về

mảng_map thực hiện chính xác điều tương tự ngoại trừ việc thay đổi tại chỗ, nó sẽ trả về một mảng mới với các phần tử được chuyển đổi.

mảng Array_filter (mảng $ input [, callback $ callback]) <- mảng trả về

Array_filter với hàm F, thay vì chuyển đổi các phần tử, sẽ loại bỏ bất kỳ phần tử nào F(x)không đúng


Không thể hiểu tại sao giá trị mảng của tôi biến mất. Nhìn vào tài liệu tôi giả sử đã array_walktrả về một mảng như array_mapvà hình dung vấn đề nằm ở chức năng của tôi. Tôi đã không nhận ra cho đến khi tôi thấy điều này rằng kiểu trả về là boolean.
Dylan Valade

22

Các câu trả lời khác thể hiện sự khác biệt giữa mảng_walk (sửa đổi tại chỗ) và mảng_map (trả lại bản sao đã sửa đổi) khá tốt. Tuy nhiên, họ không thực sự đề cập đến mảng_reduce, đây là một cách chiếu sáng để hiểu mảng_map và mảng_filter.

Hàm Array_reduce có một mảng, hàm hai đối số và 'bộ tích lũy', như thế này:

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

Các phần tử của mảng được kết hợp với bộ tích lũy cùng một lúc, sử dụng hàm đã cho. Kết quả của cuộc gọi trên giống như thực hiện điều này:

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

Nếu bạn thích suy nghĩ về các vòng lặp, thì nó giống như làm như sau (Tôi thực sự đã sử dụng điều này như một dự phòng khi mảng_reduce không có sẵn):

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

Phiên bản lặp này cho thấy rõ lý do tại sao tôi gọi đối số thứ ba là 'bộ tích lũy': chúng ta có thể sử dụng nó để tích lũy kết quả qua mỗi lần lặp.

Vậy điều này có liên quan gì với Array_map và Array_filter? Hóa ra cả hai đều là một loại mảng_reduce đặc biệt. Chúng ta có thể thực hiện chúng như thế này:

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

Bỏ qua thực tế là Array_map và Array_filter đưa các đối số của chúng theo một thứ tự khác nhau; đó chỉ là một cách giải quyết khác của PHP. Điểm quan trọng là phía bên tay phải giống hệt nhau ngoại trừ các chức năng mà tôi đã gọi là $ MAP và $ FILTER. Vậy, chúng trông như thế nào?

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

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

Như bạn có thể thấy, cả hai hàm đều lấy $ tích lũy và trả lại nó. Có hai sự khác biệt trong các chức năng này:

  • $ MAP sẽ luôn nối với $ ắc quy, nhưng $ LỌC sẽ chỉ làm như vậy nếu hàm $ (phần tử $) là TRUE.
  • $ FILTER nối thêm phần tử gốc, nhưng $ MAP nối thêm hàm $ (phần tử $).

Lưu ý rằng điều này là xa những chuyện vặt vãnh vô dụng; chúng ta có thể sử dụng nó để làm cho thuật toán của chúng ta hiệu quả hơn!

Chúng ta thường có thể thấy mã như hai ví dụ sau:

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

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

Việc sử dụng Array_map và Array_filter thay vì các vòng lặp làm cho các ví dụ này trông khá đẹp. Tuy nhiên, nó có thể rất không hiệu quả nếu đầu vào $ lớn, vì cuộc gọi đầu tiên (bản đồ hoặc bộ lọc) sẽ đi qua đầu vào $ và xây dựng một mảng trung gian. Mảng trung gian này được chuyển thẳng vào cuộc gọi thứ hai, nó sẽ đi qua toàn bộ một lần nữa, sau đó mảng trung gian sẽ cần được thu gom rác.

Chúng ta có thể thoát khỏi mảng trung gian này bằng cách khai thác thực tế là Array_map và Array_filter đều là ví dụ của mảng_reduce. Bằng cách kết hợp chúng, chúng ta chỉ phải duyệt qua các đầu vào $ một lần trong mỗi ví dụ:

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

LƯU Ý: Việc triển khai mảng_map và mảng_filter ở trên của tôi sẽ không hoạt động chính xác như của PHP, vì mảng_map của tôi chỉ có thể xử lý một mảng tại một thời điểm và mảng_filter của tôi sẽ không sử dụng "rỗng" làm hàm $ mặc định của nó. Ngoài ra, sẽ không bảo quản chìa khóa.

Không khó để khiến chúng hoạt động như của PHP, nhưng tôi cảm thấy rằng những sự phức tạp này sẽ khiến ý tưởng cốt lõi trở nên khó phát hiện hơn.


1

Bản sửa đổi sau đây tìm cách phân định rõ hơn mảng Array_filer (), Array_map () và Array_walk () của PHP, tất cả đều bắt nguồn từ lập trình hàm:

Array_filter () lọc dữ liệu, tạo ra một mảng mới chỉ giữ các mục mong muốn của mảng cũ, như sau:

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

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

mã sống ở đây

Tất cả các giá trị số được lọc ra khỏi mảng $, chỉ còn lại $ được lọc với các loại trái cây.

Array_map () cũng tạo ra một mảng mới nhưng không giống như Array_filter () mảng kết quả chứa mọi phần tử của đầu vào $ được lọc nhưng với các giá trị bị thay đổi, do áp dụng gọi lại cho từng phần tử, như sau:

<?php

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

mã sống ở đây

Mã trong trường hợp này áp dụng một cuộc gọi lại bằng cách sử dụng strtoupper tích hợp () nhưng một hàm do người dùng định nghĩa cũng là một tùy chọn khả thi khác. Cuộc gọi lại áp dụng cho mọi mục của $ được lọc và do đó tạo ra $ nu có các phần tử chứa các giá trị chữ hoa.

Trong đoạn mã tiếp theo, mảng walk () di chuyển ngang qua $ nu và thực hiện các thay đổi đối với từng phần tử để xem toán tử tham chiếu '&'. Những thay đổi xảy ra mà không tạo ra một mảng bổ sung. Mọi giá trị của phần tử thay đổi tại chỗ thành một chuỗi nhiều thông tin hơn chỉ định khóa, danh mục và giá trị của nó.

<?php

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

Xem bản demo

Lưu ý: hàm gọi lại đối với mảng_walk () có hai tham số sẽ tự động lấy giá trị của một phần tử và khóa của nó và theo thứ tự đó, khi được gọi bởi mảng_walk (). (Xem thêm tại đây ).


1
Lưu ý rằng các hàm $lambda$callbackchỉ là mở rộng của các hàm hiện có, và do đó hoàn toàn dư thừa. Bạn có thể nhận được kết quả tương tự bằng cách chuyển (tên của) chức năng cơ bản: $filtered = array_filter($array, 'ctype_alpha');$nu = array_map('strtoupper', $filtered);
Warbo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.