reduce
và reduceRight
là hai phương thức mảng JavaScript tích hợp có một chút đường cong học tập dốc.
Nhưng bản chất của các phương pháp này cũng đơn giản như các phép tính số học sau đây.
Giả sử chúng ta có một dãy số:
[1, 2, 3, 4]
Và chúng tôi muốn lấy tổng của chúng.
Các reduce
cách để có được tổng tương tự như:
((((1) + 2) + 3) + 4)
Trong khi reduceRight
cách để có được tổng tương tự như:
((((4) + 3) + 2) + 1)
Với reduce
và reduceRight
, bạn có thể xác định dấu + của riêng mình. Các phần tử của mảng cũng có thể là bất kỳ thứ gì. Nghe có vẻ thú vị, phải không?
nghĩ về reduce
và reduceRight
không là gì ngoài sự tổng quát hóa của các mẫu số học ở trên. Trong bài viết này, chúng tôi sẽ đề cập đến tất cả các chi tiết quan trọng.
Bài viết này sử dụng cách tiếp cận thuật toán dễ hiểu để cho bạn thấy cách giảm hoạt động trong JavaScript.
Tôi cũng đã tạo một video để cho bạn thấy các phương pháp này hoạt động như thế nào. Hãy xem nếu muốn tìm hiểu các khái niệm từ một góc độ trực quan hơn:
Mục lục
1Cái gì được rút gọn thành cái gì? §
Bạn có thể tự hỏi, “Loại giảm nào xảy ra khi sử dụng reduce
hoặc reduceRight
?”
Ở đây, phép rút gọn phản ánh một cách chuyển đổi cụ thể (mà chúng ta sẽ thấy chi tiết) các phần tử trong một mảng thành một giá trị duy nhất tương tự như các phép tính số học mà chúng ta đã thấy ở trên.
Nhưng lưu ý rằng giá trị đầu ra có thể là bất cứ thứ gì. Vì vậy, nó có thể là một giá trị trông lớn hơn mảng ban đầu mà phương thức được gọi.
Trong lập trình chức năng ngôn ngữ, ý tưởng giảm có nhiều tên khác như gập lại, tích trữ, tổng hợp, nén và ngay cả tiêm.
2thông số của reduce
/reduceRight
§
Cả hai phương pháp này đều có cùng quy tắc để gọi chúng. Vì vậy, thật dễ dàng để tìm hiểu chúng cùng nhau. Hãy xem làm thế nào họ có thể được gọi là:
let myArray = [/* an array */];
let callbackfn = /* A function value */ ;
let initialvalue = /* any value */ ;
myArray.reduce(callbackfn)
myArray.reduce(callbackfn, initialValue)
myArray.reduceRight(callbackfn)
myArray.reduceRight(callbackfn, initialValue)
Ở đây việc sử dụng các tham số của reduce
/reduceRight
được giải thích thông qua callbackfn
và initialValue
biến:
callbackfn
: Nó phải là một chức năng. Trong khi lặp lại mảng, đối với mỗi phần tử, reduce
/reduceRight
cuộc gọi callbackfn
với 4 đối số. Giả sử các biến previousValue
, currentElement
, index
và array
giữ các giá trị của các đối số đó, tương ứng. Vì vậy, cuộc gọi nội bộ đến callbackfn
trông như thế này:
callbackfn(previousValue, currentElement, index, array)
Bây giờ hãy xem ý nghĩa của các giá trị đó:
previousValue
: Đây còn được gọi là bộ tích điện. Tóm lại, giá trị này đại diện cho “công việc đang tiến hành” của giá trị trả về của phương thức. Giá trị này bao gồm những gì sẽ trở nên hoàn toàn rõ ràng khi bạn nghiên cứu thuật toán được trình bày ở phần sau của bài viết này.currentElement
: Phần tử hiện tại.index
: Chỉ số của phần tử hiện tại.array
:myArray
.
Giá trị trả về của callbackfn
: Đối với cuộc gọi cuối cùng đến callbackfn
giá trị trả về của nó trở thành giá trị trả về của reduce
/reduceRight
. Nếu không, giá trị trả về của nó sẽ được đưa ra là previousValue
cho cuộc gọi tiếp theo tới callbackfn
.
Và cuối cùng, initialValue
: Đây là giá trị ban đầu tùy chọn cho previousValue
(bộ tích lũy). Nếu nó được đưa ra, và myArray
có một số yếu tố trong đó, cuộc gọi đầu tiên đến callbackfn
sẽ nhận giá trị này như previousValue
.
Ghi chú: Các callbackfn
thường được gọi là một chức năng giảm tốc(hoặc chỉ bộ giảm tốc gọi tắt).
3hiểu biết reduce
/reduceRight
với sơ đồ §
Sự khác biệt duy nhất giữa reduce
và reduceRight
là hướng của vòng lặp. reduce
lặp qua các phần tử mảng từ trái sang phải. Và reduceRight
lặp qua các phần tử từ phải sang trái.
Hãy xem cách bạn có thể sử dụng reduce
/reduceRight
để tham gia một mảng các chuỗi. Lưu ý cách đạt được đầu ra cuối cùng bằng cách nối các phần tử mảng từng bước theo cả hai hướng:

reduce
và reduceRight
Ở đây lưu ý rằng:
acc
được sử dụng để truy cậppreviousValue
.curVal
được sử dụng để truy cậpcurrentElement
.- Đầu vào hình tròn để
r
đại diệncurVal
. - Đầu vào hình chữ nhật để
r
đại diệnacc
hoặc bộ tích lũy. - Các giá trị ban đầu có dạng hình chữ nhật, bởi vì chúng được nhận bởi
r
nhưacc
S.
4thuật toán của reduce
/reduceRight
§
Thuật toán 29 dòng dưới đây thoạt nhìn có vẻ đáng sợ. Nhưng bạn có thể sẽ thấy nó dễ hiểu hơn nhiều so với việc tiêu hóa hàng đống câu dài giải thích các chi tiết phức tạp của các phương pháp này.
Ghi chú: Thuật toán được mô tả ở đây có ngữ cảnh của phần “Tham số của reduce/reduceRight”. (Tức là các biến myArray
, callbackfn
và initialValue
đến từ phần đó.)
Vì vậy, hãy thư giãn, tận hưởng các bước và đừng quên thử nghiệm trong bảng điều khiển:
-
1
Nếu
initialValue
là quà tặng,-
2
Nếu
myArray
không có phần tử, -
4
Khác
-
5
Để cho
accumulator
thì là ởinitialValue
. -
6
Nếu phương pháp là
reduce
,-
7
Để cho
startIndex
là chỉ số của phần tử ngoài cùng bên trái củamyArray
.
-
-
số 8
Nếu phương pháp là
reduceRight
,-
9
Để cho
startIndex
là chỉ số của phần tử ngoài cùng bên phải củamyArray
.
-
-
-
-
10
Khác
-
11
Nếu
myArray
không có phần tử, -
13
khác nếu
myArray
chỉ có một phần tử duy nhất, -
15
Khác
-
16
Nếu phương pháp là
reduce
,-
17
Để cho
accumulator
là phần tử ngoài cùng bên trái củamyArray
. -
18
Để cho
startIndex
là chỉ số của phần tử đứng ngay sau phần tử ngoài cùng bên trái củamyArray
.
-
-
19
Nếu phương pháp là
reduceRight
,-
20
Để cho
accumulator
là phần tử ngoài cùng bên phải củamyArray
. -
21
Để cho
startIndex
là chỉ số của phần tử đứng ngay trước phần tử ngoài cùng bên phải củamyArray
.
-
-
-
-
22
-
23
Nếu phương pháp là
reduce
,-
24
Theo thứ tự từ trái sang phải, với mỗi phần tử của
myArray
sao cho nó là chỉ sối
≥startingIndex
,-
25
Bộ
accumulator
đếncallbackfn(accumulator, myArray[i], i, myArray)
.
-
-
-
26
Nếu phương pháp là
reduceRight
,-
27
Theo thứ tự từ phải sang trái, với mỗi phần tử của
myArray
sao cho nó là chỉ mụci
≤startingIndex
,-
28
Bộ
accumulator
đếncallbackfn(accumulator, myArray[i], i, myArray)
.
-
-
-
29
Trở lại
accumulator
.
Ghi chú: Một mảng có thể có độ dài lớn hơn nhưng không có phần tử. Những chỗ trống như vậy trong mảng thường được gọi là hố trong mảng. Ví dụ:
let arr = [,,,,];
console.log(arr.length);
// 4
// note that trailing comma doesn't increase the length.
// This feature enables us to add a new element quickly.
Các phương thức này chỉ gọi callbackfn
cho các yếu tố của myArray
mà thực sự tồn tại. Ví dụ: nếu bạn có một mảng như [1,,3,,5]
họ sẽ không xem xét các yếu tố không tồn tại tại các chỉ số 1
và 3
. Hãy thử đoán những gì sẽ được ghi lại sau khi chạy như sau:
[,,,3,,,4].reduce((_, cv, i) => {
console.log(i);
});
Nếu bạn nói 6
bạn đúng rồi!
⚠️ Cảnh báo: Không nên sửa đổi myArray
bên trong của callbackfn
vì nó làm phức tạp logic mã của bạn và do đó làm tăng khả năng xảy ra lỗi.
Nếu bạn đã đọc và hiểu đến đây, xin chúc mừng! Bây giờ bạn nên có một sự hiểu biết vững chắc về cách reduce
/reduceRight
làm.
Đây là thời điểm tuyệt vời để giải quyết một số vấn đề để làm quen reduce
/reduceRight
. Trước khi xem các giải pháp, hãy tự giải quyết chúng hoặc ít nhất là dành thời gian suy nghĩ về nó.
5Bài tập §
5.1Mảng lồng phẳng §
Viết một chức năng flatten
có thể làm phẳng một mảng lồng nhau.
let arr = [1, [2, [3], [[4], 5], 6]];
console.log(flatten(arr));
// [1, 2, 3, 4, 5, 6]
Dung dịch
const flatten = (arr) =>
arr.reduce((acc, curVal) =>
acc.concat(Array.isArray(curVal) ? flatten(curVal) : curVal), []);
5.2Loại bỏ các mục trùng lặp khỏi một mảng §
Viết một chức năng rmDuplicates
loại bỏ các mục trùng lặp như bên dưới:
console.log(rmDuplicates([1, 2, 2, 3, 4, 4, 4]));
// [1, 2, 3, 4]
Dung dịch
const rmDuplicates = arr =>
arr.reduce((p, c) => p.includes(c) ? p : p.concat(c), []);
5.3Đảo ngược một mảng mà không thay đổi nó §
Có một tích hợp reverse
phương pháp mảng để đảo ngược mảng. Nhưng nó làm thay đổi mảng ban đầu. Sử dụng reduceRight
để đảo ngược một mảng mà không làm biến đổi nó.
Dung dịch
let arr = [1, 2, 3];
let reversedArr = arr.reduceRight((acc, curVal) => [...acc, curVal], []);
console.log(arr);
// [1, 2, 3]
console.log(reversedArr);
// [3, 2, 1]
Lưu ý rằng bằng cách đảo ngược mảng theo cách này, bạn sẽ mất tất cả các lỗ trong mảng.
6Phần kết luận §
Khi nào reduce
/reduceRight
cuộc gọi callbackfn
trong nội bộ, chúng ta có thể gọi những kiểu gọi đó là “hành vi bình thường” và chúng ta có thể coi các tình huống khác là trường hợp cạnh. Những điều này có thể được tóm tắt trong bảng dưới đây:
Giá trị ban đầu | Số phần tử | đầu ra |
---|---|---|
Hiện nay | trường hợp cạnh: Giá trị ban đầu | |
Hiện nay | Lớn hơn 0 | hành vi bình thường |
Không có mặt | trường hợp cạnh: LoạiLỗi | |
Không có mặt | 1 | trường hợp cạnh: Yếu tố đó |
Không có mặt | Lớn hơn 1 | hành vi bình thường |
Học hỏi reduce
/reduceRight
liên quan nhiều hơn một chút so với các phương thức mảng bậc cao khác. Nhưng nó đáng để bạn dành thời gian để học nó thật tốt.
Cảm ơn bạn đã đọc! Tôi hy vọng bài viết này là hữu ích. Nếu bạn muốn, bạn có thể kiểm tra trang web của tôi và theo dõi tôi trên Twitter và LinkedIn.
Mừng giảm 😃