Ba khái niệm map, filter và reduce rất phổ biến và hay dùng trong Functional Programming (lập trình hàm). Nhưng với lập trình viên PHP, một ngôn ngữ có thể gọi là khá hổ lốn thì có thể bạn chưa bao giờ nghe tới hoặc biết nhưng chẳng bao giờ dùng.

Và những ai không dùng ba operations trên thì thường phải viết những đoạn code bốc smell như foreach lồng nhau, dùng mảng tạm, nhân đôi một mảng hay thay đổi giá trị của mảng đó khi không cần thiết.

Tại sao phải dùng map, filter và reduce

Ở lập trình hàm, lập trình viên không được đổi giá trị của variable và cũng không dùng vòng lặp. Thay vào đó họ dùng map, filter và reduce để:

  1. thay đổi hàng loạt
  2. lọc
  3. tính toán

dựa trên các thành phần trong mảng mà không cần thay đổi giá trị của chúng. Hơn nữa, ba operations trên không chỉ dùng trong lập trình hàm, chúng còn dùng để rút gọn code, làm code minh bạch hơn và ít smell hơn. Ví dụ với một bài toán đơn giản:

Cho một mảng số int ở dạng chuỗi, biến nó thành mảng số int

Dùng foreach ta sẽ có code như sau:

$strings = ['1', '2', '3', '999']; // mảng string
$ints = [];
foreach ($strings as $string) {
    $ints[] = intval($string); // thêm lần lượt các giá trị int cho mảng tạm
}

var_dump($ints); // [1, 2, 3, 999]

Nhưng với array_map thì code chỉ còn có thế này:

$strings = ['1', '2', '3', '999'];
// map hàm intval với từng phần tử trong mảng $strings
$ints = array_map('intval', $strings);

var_dump($ints); // [1, 2, 3, 999]

Ngắn gọn hơn nhiều rồi đúng không? Tiếp theo tôi sẽ giới thiệu chi tiết từng operator một.

Map của PHP – array_map

Map, filter và reduce với PHP - phần 1 - array_map 2

Nói một cách “hàn lâm” thì array_map trả về mảng đầu vào với các phần tử đã được xử lý qua một hàm callback. Usage:

array array_map ( callable $callback , array $array1 [, array $... ] )

Ta cùng tìm hiểu nó hoạt động như thế nào qua hai ví dụ thực tiễn.

Ví dụ 1:

Tạo một mảng có giá trị gấp đôi mảng int có sẵn

Dùng foreach:

$numbers = [1, 2, 3];
$x2Numbers = [];
foreach ($numbers as $number) {
    $x2Numbers[] = 2 * $number;
}

var_dump($x2Numbers); // [2, 4 ,6]

Dùng array_map:

$numbers = [1, 2, 3];
$x2Numbers = array_map(function ($value) {
    return $value * 2;
}, $numbers);

var_dump($x2Numbers); // [2, 4 ,6]

Ví dụ 2:

Ta có một mảng objects chứa điểm và tên của các sinh viên, dựa vào điểm ta cập nhật phần đánh giá học lực của sinh viên được chia làm 4 loại:
0 <= score <= 4 Loại F
4 < score <= 6 Loại D
6 < score <= 7 Loại C
7 < score < 8 Loại B
8 <= score <= 10 Loại A

Class Student:

class Student{
    public $name; // tên string
    public $score; // điểm float
    public $grade; // xếp hạng string
}

Dữ liệu đầu vào:

array(3) {
  [0]=>
  object(Student)#1 (3) {
    ["name"]=>
    string(9) "Student 1"
    ["score"]=>
    float(2.5)
    ["grade"]=>
    NULL
  }
  [1]=>
  object(Student)#2 (3) {
    ["name"]=>
    string(9) "Student 2"
    ["score"]=>
    float(7.6)
    ["grade"]=>
    NULL
  }
  [2]=>
  object(Student)#3 (3) {
    ["name"]=>
    string(9) "Student 3"
    ["score"]=>
    int(9)
    ["grade"]=>
    NULL
  }
}

Phần này tôi sẽ không viết code ví dụ bằng foreach nữa mà sẽ dùng array_map luôn:

function setGrade($student)
{
    if ($student->score >= 8) {
        $student->grade = 'A';
    } elseif ($student->score > 7) {
        $student->grade = 'B';
    } elseif ($student->score > 6) {
        $student->grade = 'C';
    } elseif ($student->score > 4) {
        $student->grade = 'D';
    } else {
        $student->grade = 'F';
    }
    return $student;
}

// map từng phần tử của mảng $students với hàm setGrade
array_map('setGrade', $students);

Kết quả là các sinh viên đã được xếp loại:

array(3) {
  [0]=>
  object(Student)#1 (3) {
    ["name"]=>
    string(9) "Student 1"
    ["score"]=>
    float(2.5)
    ["grade"]=>
    string(1) "F"
  }
  [1]=>
  object(Student)#2 (3) {
    ["name"]=>
    string(9) "Student 2"
    ["score"]=>
    float(7.6)
    ["grade"]=>
    string(1) "B"
  }
  [2]=>
  object(Student)#3 (3) {
    ["name"]=>
    string(9) "Student 3"
    ["score"]=>
    int(9)
    ["grade"]=>
    string(1) "A"
  }
}

Với cách code trên ta còn tận dụng được hàm setGrade để dùng lại nhiều lần. Bản thân tôi luôn cố gắng viết hàm hay class để dùng lại cho dù có mất nhiều thời gian hơn.

Từ giờ khi bạn muốn làm một tác vụ giống nhau với từng phần tử trong mảng, thay vì dùng foreach thì hãy suy nghĩ cách dùng array_map nhé. Để tìm hiểu chi tiết hơn bạn có thể tham khảo manual của array_map tại đây.

Vậy là phần 1 đã kết thúc, mời bạn đọc tiếp phần 2 để cùng tìm hiểu về array_filter.

4.8 4 votes
Article Rating