ความแตกต่างระหว่าง array_map, array_walk และ array_filter


373

ว่าอะไรคือความแตกต่างระหว่างarray_map, และarray_walk array_filterสิ่งที่ฉันเห็นได้จากเอกสารคือคุณสามารถส่งผ่านฟังก์ชันการโทรกลับเพื่อดำเนินการกับอาร์เรย์ที่ให้มา แต่ฉันไม่พบความแตกต่างระหว่างพวกเขา

พวกเขาทำสิ่งเดียวกันหรือไม่?
พวกเขาสามารถใช้แทนกันได้?

ฉันขอขอบคุณที่คุณช่วยเป็นตัวอย่างให้กับพวกเขาหากพวกเขาแตกต่างกัน


นี่เป็นเคล็ดลับสุดยอดสำหรับการประมวลผลอาร์เรย์ที่มีชื่อผ่าน array_reduce () ควรอ่านถ้าคุณกำลังตรวจสอบ array_map, array_walk และ array_filter stackoverflow.com/questions/11563119/…
Lance Cleveland

คำตอบ:


564
  • การเปลี่ยนค่า:
    • array_mapไม่สามารถเปลี่ยนค่าภายในอาร์เรย์อินพุตได้ในขณะที่array_walkสามารถ โดยเฉพาะarray_mapอย่าเปลี่ยนข้อโต้แย้งของมัน
  • การเข้าถึงคีย์อาร์เรย์:
    • array_mapไม่สามารถทำงานกับคีย์อาร์เรย์array_walkได้
  • มูลค่าส่งคืน:
    • array_mapส่งกลับอาร์เรย์ใหม่ผลตอบแทนเท่านั้นarray_walk ดังนั้นหากคุณไม่ต้องการที่จะสร้างอาร์เรย์เป็นผลมาจากภายในหนึ่งอาร์เรย์คุณควรใช้truearray_walk
  • วนซ้ำหลายอาร์เรย์:
    • array_mapยังสามารถรับจำนวนอาเรย์โดยพลการและสามารถย้ำให้มันขนานกันในขณะที่array_walkทำงานเพียงอันเดียว
  • การส่งผ่านข้อมูลโดยพลการไปยังการโทรกลับ:
    • 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
)

3
คู่มือ PHP บอกว่า: "array_walk (): อาจมีการเปลี่ยนแปลงเฉพาะค่าของอาเรย์เท่านั้น"
feeela

10
"array_map ไม่สามารถทำงานกับคีย์อาร์เรย์" นี่ไม่เป็นความจริง:array_map(callback($key, $value), array_keys($array), $array)
Jarek Jakubowski

12
มันยังคงไม่เข้าถึงคีย์ของอาเรย์ใด ๆ แต่ก็เป็นการหาค่าที่คุณใส่ในอาเรย์ที่คุณสร้างขึ้นจากคีย์ มันเป็นวิธีแก้ปัญหามันไม่ได้คัดค้านคำสั่ง
inarilo

ในขณะที่ array_map ไม่ได้เปลี่ยนแปลงค่าโดยปริยายโดยการกำหนดผลลัพธ์ให้กับอาร์เรย์เดียวกันนั้นโดยทั่วไปจะเปลี่ยนแปลงค่านั้นและ array_walk 'ที่ขัดแย้งกัน' ซึ่งทำงานในอาร์เรย์เดียวกันนั้นเองจะไม่เปลี่ยนค่าของมันโดยตรงยกเว้นค่าที่ส่งโดยอ้างอิง (อาร์เรย์ เดินอาจลบดัชนี / องค์ประกอบเป็น array_filter ทางอ้อมผ่านฟังก์ชั่นที่ไม่ระบุชื่อใช้ข้อผ่านอาร์เรย์เดิม แต่มันเป็นวิธีแก้ปัญหา) เพื่อสรุปดังนั้นการเปลี่ยนแปลงค่าหรือถ้าค่าถูกส่งคืนหรือส่งผ่านโดยการอ้างอิงมีความแตกต่างน้อยกว่าอย่างมีประสิทธิภาพ แต่อาร์เรย์เดินทำงานได้กับดัชนีและแผนที่อาร์เรย์ที่มีหลายอาร์เรย์
FentomX1

ยิ่งไปกว่านั้นดูเหมือนว่าไม่ว่าการเดินของอาเรย์จะใช้พารามิเตอร์อาเรย์เป็นตัวอ้างอิงตัวแรกเมื่อใครต้องการที่จะเปลี่ยนมันเขาก็ต้องผ่านค่าไอเทมโทรกลับเป็นข้อมูลอ้างอิงด้วย
FentomX1

91

แนวคิดของการทำแผนที่ฟังก์ชั่นไปยังอาร์เรย์ของข้อมูลมาจากการเขียนโปรแกรมการทำงาน คุณไม่ควรนึกถึงarray_mapการforeachวนซ้ำที่เรียกใช้ฟังก์ชันในแต่ละองค์ประกอบของอาร์เรย์ (แม้ว่าจะเป็นวิธีการใช้งานก็ตาม) มันควรจะคิดว่าเป็นการใช้ฟังก์ชั่นกับแต่ละองค์ประกอบในอาเรย์อย่างอิสระ

ในทางทฤษฎีสิ่งต่าง ๆ เช่นการทำแผนที่ฟังก์ชั่นสามารถทำได้ในแบบคู่ขนานเนื่องจากฟังก์ชั่นที่ใช้กับข้อมูลควรมีผลกระทบต่อข้อมูลและไม่ได้อยู่ในสถานะโลก นี่เป็นเพราะarray_mapสามารถเลือกคำสั่งใด ๆ ที่จะใช้ฟังก์ชั่นกับรายการใน (แม้ว่าจะไม่ได้อยู่ใน PHP ก็ตาม)

array_walkในทางกลับกันมันเป็นวิธีที่ตรงกันข้ามกับการจัดการอาร์เรย์ของข้อมูล แทนที่จะจัดการแต่ละรายการแยกต่างหากมันใช้สถานะ ( &$userdata) และสามารถแก้ไขรายการในสถานที่ (คล้ายกับวง foreach) เนื่องจากแต่ละครั้งที่ไอเท็ม$funcnameถูกนำไปใช้มันสามารถเปลี่ยนแปลงสถานะโกลบอลของโปรแกรมและดังนั้นจึงต้องการวิธีการประมวลผลไอเท็มที่ถูกต้องเพียงวิธีเดียว

ย้อนกลับไปในดินแดน PHP array_mapและarray_walkเกือบเหมือนกันยกเว้นarray_walkให้คุณควบคุมการวนซ้ำของข้อมูลได้มากขึ้นและโดยปกติจะใช้เพื่อ "เปลี่ยน" ข้อมูลในสถานที่เทียบกับคืนอาร์เรย์ "เปลี่ยน" ใหม่

array_filterเป็นแอปพลิเคชั่นarray_walk(หรือarray_reduce) จริง ๆ และมีให้มากหรือน้อยเพื่อความสะดวก


5
+1 สำหรับความเข้าใจในย่อหน้าที่ 2 ของคุณ "ในทางทฤษฎีสิ่งต่าง ๆ เช่นการทำแผนที่ฟังก์ชั่นสามารถทำคู่ขนานกันได้เนื่องจากฟังก์ชั่นที่ใช้กับข้อมูลควรมีผลต่อข้อมูลเท่านั้นและไม่ใช่สถานะโลก" สำหรับเราโปรแกรมเมอร์คู่ขนานนั่นเป็นสิ่งที่ควรคำนึงถึง
etherice

คุณอธิบายได้ไหมว่าarray_filter()จะสามารถนำไปใช้งานได้array_walk()อย่างไร
pfrenssen

40

จากเอกสาร

บูล array_walk (อาร์เรย์ & $ อาร์เรย์, เรียกกลับ $ funcname [, ผสม $ userdata]) <-return bool

array_walkใช้เวลาอาร์เรย์และฟังก์ชั่นFและปรับเปลี่ยนได้โดยการเปลี่ยนทุกองค์ประกอบ x F(x)กับ

array array_map (โทรกลับ $ โทรกลับอาร์เรย์ $ arr1 [, อาร์เรย์ $ ... ]) <- กลับอาร์เรย์

array_mapทำสิ่งเดียวกันทุกประการยกเว้นว่าจะทำการแก้ไขแบบแทนที่มันจะส่งคืนอาร์เรย์ใหม่ที่มีองค์ประกอบที่แปลงแล้ว

array array_filter (array $ input [, callback $ callback]) <- กลับอาร์เรย์

array_filter ที่มีฟังก์ชั่นFแทนที่จะเปลี่ยนองค์ประกอบจะลบองค์ประกอบใด ๆ ที่F(x)ไม่เป็นความจริง


ไม่ทราบสาเหตุที่ค่าอาร์เรย์ของฉันหายไป ดูเอกสารฉันคิดว่าarray_walkคืนอาร์เรย์เหมือนarray_mapและคิดว่าปัญหาอยู่ในฟังก์ชันของฉัน ไม่ทราบจนกระทั่งฉันเห็นสิ่งนี้ว่าประเภทการคืนเป็นบูลีน
Dylan Valade

22

คำตอบอื่น ๆ แสดงให้เห็นถึงความแตกต่างระหว่าง array_walk (การแก้ไขในสถานที่) และ array_map (กลับมาแก้ไขสำเนา) ค่อนข้างดี อย่างไรก็ตามพวกเขาไม่ได้กล่าวถึง array_reduce ซึ่งเป็นวิธีการส่องสว่างที่จะเข้าใจ array_map และ array_filter

ฟังก์ชั่น array_reduce ใช้อาร์เรย์ฟังก์ชั่นสองข้อโต้แย้งและ 'สะสม' เช่นนี้:

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

องค์ประกอบของอาร์เรย์จะรวมกับตัวสะสมหนึ่งครั้งโดยใช้ฟังก์ชั่นที่กำหนด ผลลัพธ์ของการโทรด้านบนเหมือนกับการทำสิ่งนี้:

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

เวอร์ชันลูปนี้ทำให้ชัดเจนว่าทำไมฉันจึงเรียกอาร์กิวเมนต์ที่สามเป็น 'ตัวสะสม': เราสามารถใช้มันเพื่อสะสมผลลัพธ์ผ่านการวนซ้ำแต่ละครั้ง

ดังนั้นสิ่งนี้เกี่ยวข้องกับ 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 จุดสำคัญคือด้านขวามือเหมือนกันยกเว้นฟังก์ชั่นที่ฉันเรียกว่า $ MAP และ $ FILTER แล้วพวกมันมีหน้าตาเป็นอย่างไร?

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

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

อย่างที่คุณสามารถเห็นได้ฟังก์ชั่นทั้งสองใช้เวลาในการ $ สะสมและส่งกลับมาอีกครั้ง ฟังก์ชันเหล่านี้มีความแตกต่างกันสองประการ:

  • $ MAP จะผนวกเข้ากับ $ accumulator เสมอ แต่ $ FILTER จะทำเช่นนั้นก็ต่อเมื่อฟังก์ชัน $ ($ องค์ประกอบ) เป็น TRUE
  • $ FILTER ต่อท้ายองค์ประกอบดั้งเดิม แต่ $ MAP ผนวก $ function ($ องค์ประกอบ)

โปรดทราบว่านี่อยู่ไกลจากเรื่องไม่สำคัญไร้ประโยชน์ เราสามารถใช้มันเพื่อทำให้อัลกอริทึมของเรามีประสิทธิภาพมากขึ้น!

เรามักจะเห็นโค้ดเหมือนสองตัวอย่างนี้:

// 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 แทนการวนซ้ำทำให้ตัวอย่างเหล่านี้ดูค่อนข้างดี อย่างไรก็ตามมันจะไม่มีประสิทธิภาพมากถ้า $ inputs มีขนาดใหญ่เนื่องจากการโทรครั้งแรก (แผนที่หรือตัวกรอง) จะข้าม $ input และสร้างอาร์เรย์กลาง อาร์เรย์กลางนี้จะถูกส่งตรงไปยังการเรียกครั้งที่สองซึ่งจะย้อนกลับไปสู่สิ่งทั้งหมดอีกครั้งจากนั้นอาร์เรย์กลางจะต้องถูกรวบรวมขยะ

เราสามารถกำจัดอาร์เรย์กลางนี้ได้โดยใช้ประโยชน์จากข้อเท็จจริงที่ว่า array_map และ array_filter เป็นทั้งสองตัวอย่างของ array_reduce โดยการรวมเข้าด้วยกันเราจะต้องสำรวจ $ อินพุตหนึ่งครั้งในแต่ละตัวอย่าง:

// 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 ด้านบนจะไม่ทำงานเหมือนกับ PHP เพราะ array_map ของฉันสามารถจัดการได้ครั้งละหนึ่งอาร์เรย์เท่านั้นและ array_filter ของฉันจะไม่ใช้ "empty" เป็นฟังก์ชัน $ เริ่มต้น นอกจากนี้จะไม่รักษากุญแจ

ไม่ใช่เรื่องยากที่จะทำให้พวกเขาประพฤติตนเหมือน PHP แต่ฉันรู้สึกว่าภาวะแทรกซ้อนเหล่านี้จะทำให้แนวคิดหลักยากขึ้น


1

การแก้ไขดังต่อไปนี้พยายามที่จะอธิบายอย่างชัดเจนยิ่งขึ้นของ array_filer (), PHP, array_map () และ array_walk () ของ PHP ซึ่งทั้งหมดมาจากการเขียนโปรแกรมการทำงาน:

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 () อาร์เรย์ส่งผลให้มีทุกองค์ประกอบของการป้อนข้อมูล $ กรอง แต่มีค่าเปลี่ยนแปลงเนื่องจากการใช้โทรกลับไปยังแต่ละองค์ประกอบดังต่อไปนี้:

<?php

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

รหัสสดที่นี่

รหัสในกรณีนี้ใช้การโทรกลับโดยใช้ strtoupper ในตัว () แต่ฟังก์ชั่นที่ผู้ใช้กำหนดเองก็เป็นอีกทางเลือกหนึ่งที่ใช้งานได้เช่นกัน การเรียกกลับใช้กับทุกรายการของ $ filter และทำให้ Engu $ nu ซึ่งองค์ประกอบมีค่าตัวพิมพ์ใหญ่

ในข้อมูลโค้ดถัดไป array walk () สำรวจราคา $ nu และทำการเปลี่ยนแปลงองค์ประกอบแต่ละอย่าง การเปลี่ยนแปลงเกิดขึ้นโดยไม่ต้องสร้างอาร์เรย์เพิ่มเติม ค่าขององค์ประกอบทุกค่าจะเปลี่ยนเป็นสตริงข้อมูลที่ระบุคีย์ประเภทและค่าของมัน

<?php

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

ดูตัวอย่าง

หมายเหตุ: ฟังก์ชั่นการโทรกลับด้วยความเคารพ array_walk () ใช้สองพารามิเตอร์ซึ่งจะได้รับค่าขององค์ประกอบและที่สำคัญของมันและในลำดับนั้นโดยอัตโนมัติเช่นกันเมื่อเรียกโดย array_walk () (ดูเพิ่มเติมที่นี่ )


1
โปรดทราบว่าฟังก์ชั่น$lambdaและ$callbackเป็นเพียงการขยายตัวของฟังก์ชั่นที่มีอยู่และจึงซ้ำซ้อนอย่างสมบูรณ์ คุณสามารถได้รับผลลัพธ์เดียวกันโดยผ่าน (ชื่อของ) ฟังก์ชันพื้นฐาน: $filtered = array_filter($array, 'ctype_alpha');และ$nu = array_map('strtoupper', $filtered);
Warbo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.