[REDUCE] JavaScript Array reduce()

https://niithanoi.edu.vn/reduce-trong-javascript.html

JavaScript Reduce. Đây chính là một method không phức tạp nhưng thật sự là khó hiểu và không biết áp dụng khi nào trong ứng dụng của developers. Kể cả ngay những coder giỏi đôi khi cũng cảm thấy thực sự bối rối khi sử dụng reduce javascript method.

arr.reduce(reducer, initialValue);

Lưu ý #1: initialValue (giá trị khởi tạo) là tùy chọn. Nếu bạn truyền vào đối

Giải thích về reduce() trong JavaScript
22/01/2021 - 10:36 AM
Cỡ chữ  
 
Chào bạn, reduce() trong JavaScript là một phương thức có thể khó hiểu đối với những người mới bắt đầu, đặc biệt là khi các giải thích trên mạng cũng rất mơ hồ.


Do đó, trong bài viết này, mình sẽ giúp bạn tìm hiểu về căn bản của phương thức reduce() trong JavaScript qua các ví dụ từ đơn giản đến phức tạp.


reduce() trong JavaScript


Hiểu được cách sử dụng reduce() có rất nhiều lợi ích sau này bởi khi học Front end (chuyên sâu), bạn sẽ sử dụng reduce() thường xuyên để quản lý state (hãy nghĩ đến Redux).


Cú pháp đơn giản của phương thức reduce() trong JavaScript là:



arr.reduce(reducer, initialValue);
 


> Lưu ý #1: initialValue (giá trị khởi tạo) là tùy chọn. Nếu bạn truyền vào đối số khởi tạo, phương thức reducer() sử dụng nó ở lần gọi đầu tiên.


Phương thức reduce() là gì?


Phương thức reduce() là:


Phương thức giảm mảng xuống thành một giá trị duy nhất.
Phương thức reduce() thực thi một hàm được cung cấp cho mỗi giá trị của mảng (từ trái sang phải).
Giá trị trả về của hàm được lưu trữ trong bộ tích lũy (kết quả / tổng).


> Lưu ý #2: reduce() không thực thi hàm đối với các phần tử mảng không có giá trị.

> Lưu ý #3: Phương thức này không thay đổi mảng ban đầu.


Reduce đi kèm với một số thuật ngữ như reducer và accumulator. Trong đó:


accumulator là giá trị mà chúng ta kết thúc
reducer là hành động mà chúng ta sẽ thực hiện để đạt được một giá trị.


Bạn phải nhớ rằng một reduce() sẽ chỉ trả về một giá trị duy nhất do đó nó tên là reduce.


Hàm call back truyền vào trong reduce()


Hàm call back (reducer) này có bốn đối số:



function reducer(accumulator, currentValue, currentIndex, array) {}
 


Trong đó:


accumulator:  Bắt buộc. Giá trị được trả về từ lần gọi trước. Nếu bạn truyền giá trị khởi tạo cho phương thức reduce(), khi hàm call back này được thực thi lần đầu tiên, accumulator bằng giá trị khởi tạo.
currentValue: Bắt buộc. Giá trị của phần tử mảng trong lần lặp hiện tại.
currentIndex: Tùy chọn. Chỉ số của phần tử mảng trong lần lặp hiện tại.
arr: Tùy chọn. Mảng mà phương thức reduce() gọi và áp dụng hàm call back trên từng phần tử của nó.


Hàm reducer này thực thi trên mỗi phần tử và trả về một giá trị. Giá trị trả về này được gán cho đối số tích lũy (accumulator) trong mỗi lần lặp.


Ở lần lặp cuối cùng, giá trị của accumulator trở thành giá trị kết quả duy nhất.


Lấy ví dụ cổ điển sau:



let tong = 0;

const mangSo = [5, 10, 15];

for (let i = 0; i < mangSo.length; i++) {
    tong += mangSo[i];
}
console.log(tong);
 


Kết quả:



30
 


Chương trình trên hoạt động tốt và tong có kết quả 30.


Nhưng chúng ta cũng có thể làm điều này với phương thức reduce() mà không làm thay đổi giá trị khởi tạo (Biến mà chúng ta gọi là initialValue (giá trị khởi tạo) trong cú pháp)



// Đây là giá trị khởi tạo của chúng ta
const giaTriKhoiTao = 0;

// Mảng số
const mangSo = [5, 10, 15];

// Tạo hàm tính tổng. Hành động này lát nữa sẽ
// áp dụng lên trên mảng (hàm này sẽ được gọi lại)
const tinhTong = (boTichLuy, phanTuHienTai) => {
    return boTichLuy + phanTuHienTai;
};

// Sử dụng reduce()
const tong = mangSo.reduce(tinhTong, giaTriKhoiTao);
console.log(tong);
 


Kết quả:



30
 


Chương trình trên nhìn có vẻ khó hiểu, nhưng chúng ta thêm console vào trong hàm tinhTong như sau:



// Đây là giá trị khởi tạo của chúng ta
const giaTriKhoiTao = 0;

/* Mảng số */
const mangSo = [5, 10, 15];

// Tạo hàm tính tổng. Hành động này lát nữa sẽ
// áp dụng lên trên mảng (hàm này sẽ được gọi lại)
const tinhTong = (boTichLuy, phanTuHienTai) => {
    console.log("Bộ tích lũy: " + boTichLuy);
    console.log("Phần hiện tại: " + phanTuHienTai);
    console.log("---");
    return boTichLuy + phanTuHienTai;
};

// Sử dụng reduce()
const tong = mangSo.reduce(tinhTong, giaTriKhoiTao);
console.log(tong);
 


Kết quả:



Bộ tích lũy: 0
Phần hiện tại: 5
---
Bộ tích lũy: 5
Phần hiện tại: 10
---
Bộ tích lũy: 15
Phần hiện tại: 15
---
30
 


Như vậy ta thấy, phương thức của chúng ta được gọi 3 lần vì mảng có 3 phần tử.


boTichLuy bắt đầu từ 0, đó là giá trị khởi tạo mà chúng ta đã truyền vào reduce().


Trên mỗi lần gọi hàm các phần tử hiện tại sẽ được cộng vào bộ tích lũy (là accumulator đó).


Cuối cùng, chúng ta có kết quả 30.


> Lưu ý #4: Hãy nhớ là hàm reduce() chỉ trả về giá trị duy nhất


Một ví dụ khác khá phổ biến trong lập trình web, giả sử chúng ta có giỏ hàng như sau:



// Giỏ hàng
let gioHang = [{
        sanPham: "iPhone 12",
        soLuong: 1,
        giaBan: 999
    },
    {
        sanPham: "RAM",
        soLuong: 1,
        giaBan: 50
    },
    {
        sanPham: "AirPods",
        soLuong: 2,
        giaBan: 250
    }
];
 


Bây giờ, nhiệm vụ của chúng ta là tính tổng giá trị của giỏ hàng ngày bằng cách lấy số lượng * giá bán.


Chúng ta sử dụng phương thức reduce() như sau:



// Tính tổng tiền phải trả bằng reduce()
let tongTien = gioHang.reduce(function(tong, sp) {
    return tong + sp.soLuong * sp.giaBan;
}, 0);

// In ra tổng tiền của giỏ hàng
console.log(tongTien);
 


Kết quả ta được:



1549
 


> Lưu ý #5: Nếu bạn không truyền giá trị khởi tạo, lần lặp đầu tiên, nó sẽ lấy phần tử đầu tiên (1 đối tượng) để tính cộng => sẽ tính toán sai.


> Nếu bạn muốn học lập trình web nhanh hơn thì có thể tham gia KHÓA HỌC JAVA WEB hoặc HỌC LÀM WEB PHP (Dành cho sinh viên CNTT, người đi làm) trong 4.5 tháng.

> Hoặc KHÓA HỌC LẬP TRÌNH WEB (Full stack) dành cho dân ngoại đạo trong 12 tháng


Trên đây là một vài ví dụ đơn giản về phương thức reduce(), bây giờ chúng ta sẽ thử với ví dụ phức tạp hơn.


Làm phẳng một mảng bằng cách sử dụng reduce()


Giả sử chúng ta có mảng sau:



const mangSo = [1, 2, [3, 10, [11, 12]], [1, 2, [3, 4]], 5, 6];
 


Và giả sử vì một số lý do điên rồ, JavaScript đã loại bỏ phương thức .flat, thế nên chúng ta phải tự làm phẳng mảng này.


Chúng ta sẽ viết một hàm để làm phẳng bất kỳ mảng nào bất kể các mảng được lồng sâu như thế nào:



function lamPhangMang(mang) {
    // Giá trị khởi tạo của chúng ta
    // trong trường hợp này là 1 mảng trống
    const giaTriKhoiTao = [];

    // Gọi reduce() trên mảng của chúng ta
    return mang.reduce((ketQua, giaTri) => {
        // Nếu giá trị là 1 mảng thì gọi đệ quy reduce
        // Nếu giá trị không phải 1 mảng thì cần nối
        // các giá trị lại với nhau
        return ketQua.concat(Array.isArray(giaTri) ? lamPhangMang(giaTri) : giaTri);
    }, giaTriKhoiTao);
}

const mangSo = [1, 2, [3, 10, [11, 12]], [1, 2, [3, 4]], 5, 6];

// Làm phẳng mảng lồng sâu
console.log(lamPhangMang(mangSo));
 


Kết quả:



[1, 2, 3, 10, 11, 12, 12, 3, 4, 5, 6]
 


Việc làm phẳng mảng khá phổ biến đó, với reduce thì việc này cũng đã đơn giản đi hơn rất nhiều.


Thay đổi cấu trúc đối tượng với reduce()


Giả sử, máy chủ gửi cho chúng ta một đối tượng như thế này:



const duLieu = [
    { name: "Apple", country: "Mỹ" },
    { name: "Samsung", country: "Hàn Quốc" },
    { name: "Google", country: "Mỹ" }
];
 


Và chúng ta muốn chuyển đối tượng này thành như thế này:



const duLieuDaThayDoi = {
    Apple: { country: "Mỹ" },
    Samsung: { country: "Hàn Quốc" },
    Google: { country: "Mỹ" }
};
 


Để có được kết quả mong muốn, chúng ta thực hiện như sau:



const thayDoiCauTruc = data =>
    data.reduce((acc, item) => {
        // Thêm key cho đối tượng
        acc[item.name] = { country: item.country };
        return acc;
    }, {});

console.log(thayDoiCauTruc(duLieu));
 


Tổng kết về reduce()


Như vậy, trong bài hướng dẫn này, mình đã giúp bạn tìm hiểu sơ bộ về cách hoạt động của phương thức reduce() trong JavaScript. Hi vọng nó giúp ích cho bạn.


Chúc bạn học tốt!

Last updated

Navigation

Lionel

@Copyright 2023