Cá nhân mình thấy method reduce() trong JS được ứng dụng rất nhiều, và nói thật cá nhân mình lúc mới tìm hiểu reduce thấy khá vất vả, search trên mạng khá nhiều rồi mới thấm được ít. Chính vì lẽ đó hôm này mình sẽ viết một bài dựa trên hiểu biết (đã tìm hiểu cộng với áp dụng) cộng thêm với dịch thuật từ một số trang web uy tín về JS để giải thích rõ hơn về method reduce() cũng như các (cách) ứng dụng của nó nhé. Oke, let’s go.
Khi hiểu rõ bản chất một thứ gì rồi, thì các vấn đề liên quan tới nó sẽ tự nhiên biến mất. Người ta sợ hãi những thứ họ không hiểu và lãng tránh nó để rồi mất một cơ hội để … “làm quen”. Lang man là vậy đó. Nhập đề ăn rơ lại nhé. Cú pháp của method reduce này như sau:
arr.reduce(callback, initialValue);
Thuật ngữ cơ bản
Tìm hiểu reduce() thì các bạn sẽ thấy có 2 thuật ngữ:
reducer(thực ra là một callback, tham số đầu tiên của hàmreduce)accumulator(biến cộng dồn, tham số đầu tiên của callback reducer).
Bạn tưởng tượng nhé reduce nó giống như một cái vòng lặp, ví dụ mảng [1, 2, 3, 4, ..., 10] nó sẽ lặp (từ trái lần lần sang phải) đến hết mảng (1 -> 10). Với mỗi lần lặp, reduce sẽ gọi callback reducer.
Reducer và các tham số
Callback reducer nhận hai tham số (thực tế 4 nhưng phạm vi bài viết chúng ta tìm hiểu 2 tham số quan trọng nhất):
accumulator- biến cộng dồn nói phía trêncurrentItem- giá trị hiện tại đang lặp
Và chỉ trả về duy nhất một giá trị. Giá trị này sẽ được truyền vào tham số accumulator của callback reducer ở lần gọi tiếp theo, nếu hết vòng lặp (không gọi reducer nữa) thì trả về giá trị này.
Ví dụ 1:
1 | var array = [1, 2, 3, 4, 5]; |
Ví dụ 2:
1 | var array = [9, 2, 3, 4, 5]; |
Ví dụ 3:
1 | var array = [9, 2, 3, 4, 5]; |
initialValue - giá trị khởi tạo
Giá trị khởi tạo cho callback reducer, giá trị này được truyền lúc gọi callback.
Khi gọi callback reducer mà bản thân cái mảng không có phần tử nào cộng với việc không có initialValue thì sẽ bị lỗi
1 | var array = []; |
Ứng dụng của reduce
1 | var value = 0; |
Kết quả in ra là 30.
Hàm này với mỗi lần lặp sẽ thay đổi giá trị biến value. Nhìn chung cách cũng ổn cơ mà xét về mặt pure function thì nó đã tác động đến biến (value) bên ngoài phạm vi vòng lặp. Nên để tránh ảnh hưởng đến biến value ta sẽ sử dụng reduce(). Kết quả in ra sẽ tương tự nhau.
Cách dùng reduce sẽ như vầy:
1 | /* giá trị mặc định đầu tiên cho nó, giống value phía trên, giá trị mặc định đầu tiên là 0 */ |
Code ở trên rất đơn giản không có phức tạp cho lắm, lúc này bạn mở developer tool ra check xem console.log trong reducer in ra cái gì nhé.
Nó sẽ in ra như thế này.
Chúng ta sẽ đi phân tích chút xíu về cơ chế chạy chỗ này, mình sẽ mô phỏng 4 bước của quá trình chạy nhé:
- Vì mảng có 3 phần tử, nó sẽ chạy 3 lần. Ở lần chạy đầu tiên
accumulatorsẽ có giá trị là0, giá trị này là từinitialValuetruyền vào thông qua methodreduce. VàcurrentItemlúc này là5sẽ cộng vớiaccumulator(accumulatorlúc này là0), return ra5kết quả return của reducer sẽ “tự động cộng dồn” vàoaccumulator(thực ra cơ chế là nó lấy giá trị return này pass vào tham sốreducerlần nữa). - Lần 2
accumulatorđang có giá trị là5(kết quả từ bước 1), cộng vớicurrentItemlúc này là10, return về 15. - Lần chạy cuối cùng tương tự bước 2,
currentItemlúc này là15cộng vớiaccumulatorgiá trị là15return về30. - Vì đã hết mảng nên return giá trị
accumulatorvề.
Đó là ví dụ đơn giản về reduce. Và sau đây mình sẽ nâng cao lên một chút.
Nâng cao chút - Ứng dụng reduce với array (flatten array)
Ví dụ này từ bài viết của mình
Ứng dụng reduce vào flatten mảngMình có mảng như sau:
1 | var arr = [ 1, 2, 3, [ 1, 2, 3, 4, [ 2, 3, 4 ] ] ]; |
Và nếu JS trao cho mình cơ hội được viết lại hàm .flat thì mình sẽ dùng viết lại như ri:
1 | function flatArray(arr) { |
Dùng như sau:
1 | var flatten = flatArray(arr); |
Oke, xong ví dụ đơn gian thứ hai rồi nha.
Ứng dụng reduce với re-structure object
Giả sử mình có object như thế này:
1 | var usersInfo = [ |
Và mình muốn nó re-structure lại như vầy:
1 | var usersInfo = { |
Vậy ứng dụng reduce mình sẽ làm như thế nào nhỉ? Các bạn suy nghĩ chứ khoan kéo xuống phần mình làm nha. 3 phút nghĩ suy bắt đầu.
Đây là phần mình giải quyết:
1 | var objectMapFromArray = (arr) => arr.reduce((accumulator, currentItem) => { |
Gọi sử dụng:
1 | objectMapFromArray(usersInfo); |
Và đây là kết quả:
Summary
Qua bài ni, mình hi vọng bạn đã hiểu cách thức hàm này hoạt động. Oh yeah.
Updated date: 21.03.2020