11/08/2018, 20:41

Tiêu hóa ba "lệnh" khó xơi map, filter, reduce với bí kíp gia truyền nhà họ Mòe

trong bài có xài cái bánh mì của Duy Khánh và đống thức ăn của Da Peng function mũi tên trong bài này sẽ xài function mũi tên, ai chưa biết thì nó có dạng như sau tiêu thức Function tiền sử Function mũi tên hình thức function( thông số ){ return cái cần return ;} ( ...

trong bài có xài cái bánh mì của Duy Khánh và đống thức ăn của Da Peng

function mũi tên

trong bài này sẽ xài function mũi tên, ai chưa biết thì nó có dạng như sau

tiêu thức Function tiền sử Function mũi tên
hình thức function(thông số){ return cái cần return;} (thông số) => cái cần return
chú ý: nếu có 1 thông số thì func mũi tên không cần bỏ thông số vào trong ngoặc đơn

có thể thấy function mũi tên sinh ra để đỡ phải nói nhiều mà còn dễ nhìn dễ hiểu. (khó ban đầu khi chuyển từ hình thức cũ sang mới, nhưng biết rồi thì thích)

dùng for of trong bài này

trong bài này để tiện so sánh mình sẽ chỉ lấy thằng for of chứ không lôi cả thằng for cổ điển mà hay đi kèm với chỉ số chạy i vào, để việc so sánh ngắn gọn hơn.

cú pháp 2 thằng khác nhau như sau

for cổ điển

là một vòng lặp đa mục đích với dạng chung như sau:

for (i=0;i< bao nhiêu;i++){
điều cần làm lên ai đó;
}

trong bài này để rà từng thằng trong array thì chúng ta xài thằng for cổ điên như sau:

for (i=0; i<arr.length;i++){
điều cần làm lên từng thằng arr[i];
}

còn for of cho bài này như sau

for (x of arr){
điểu cần làm lên từng thằng x;
}

(lưu ý chữ x là mình tự đặt, các bạn thích đặt là y là z là conchuot hay là gì cũng được)
(x tương đương với arr[i] của for tương đương với từng thằng một trong arr, dùng x rồi nên đoạn điều cần làm lên từng thằng x là "thằng x" chứ không phải thằng arr[i] vì for of làm méo gì có i, haha)

Vì bổn phận của for of sinh ra đã được thiết kế để nghịch từng thằng một nên for of không cần chỉ số chạy, còn for thì làm nó sinh ra để nhiều mục đích khác nhau, nên nó phải có chỉ số chạy để ví dụ như khi cần nghịch 1 thằng, skip 1 thằng, rồi lại nghịch 1 thằng, rồi lại skip 1 thằng (i=0;i<arr.length;i+=2).... thì for làm được chứ for of không làm được

Nhưng trong bài này thì mục đích của chúng ta là nghịch từng thằng một trong array for of thích hợp để đưa vào cuộc hơn.

Một số từ vựng khó hiểu

method

method chính là method map, method filter, method reduce...

method cũng là một "function" nhưng method thuộc về object nào đó, còn function thì tự do.

function như 1 cô gái, method như 1 người vợ, 1 cô gái là 1 người vợ khi cô ấy thuộc về 1 người đàn ông (object) nào đó.

Vậy có thể nói, mỗi method đều là một "function", nhưng không phải function nào cũng là method. (Mỗi người vợ đều là một cô gái, nhưng không phải cô gái nào cũng là một người vợ)

callback function

1 callback function là 1 function được sử dụng làm argument (phần trong dấu ngoặc đơn) của 1 function/method khác.

Không biết có ai như mình không chứ cứ nhìn cái từ "callback" mình lại nghĩ đến "gọi lại (cuộc gọi nhỡ)", cố gắng mãi mà không khớp được cái tên "callback" với cái ý nghĩa thật sự.

Trong bài này callback function sẽ là những hành động cụ thể mà method map, filter, reduce... làm với từng thằng một trong array nhé!

tèn ten ten ten, tiếp theo đi thẳng đến so sánh giữa for of, map, filter và reduce

tổng quan

alt text
||for of|map|filter|reduce|
|---|---|---|---|---|
|năng lực|rà từng thằn một và làm gì đó với nó|rà từng thằng một trong arr rồi clone nó rồi chỉnh sửa cái clone rồi cuối cùng xuất ra một arr mới với số lượng thành viên bằng arr cũ nhưng chứa toàn clone (với chỉnh sửa)|rà từng thằng một trong arr rồi bốc ra những thằng đạt yêu cầu|cầm một con số khởi điểm đi rà từng thằng một trong arr, cho con số khởi điểm nhập vào thằng đó rồi chiếm luôn vị trí, lần lượt cho đến hết arr|
|đầu vào|1 arr hoặc 1 string hoặc 1 loạt thông số hoặc 1 nodelist... và 1 callback function|1 arr và 1 callback function|1 arr và 1 callback function|1 arr và 1 callback function và 1 con số khởi điểm|
|return của callback function|có thể bất cứ thứ gì| 1 thành viên|1 boolean với ý nghĩa quyết định (nếu là true thì tức là lời thông báo "thằng này đậu" và ngược lại false là trượt|1 con số|
|return của toàn bộ method|có thể là bất cứ thứ gì| 1 arr bằng chiều dài của arr cũ| 1 arr gồm các thành viên cũ mà đạt yêu cầu, người đạt yêu cầu không thể nhiều hơn tổng số người bị khám nên arr mới luôn ngắn hơn arr cũ| 1 con số|
|số phận arr gốc|luật rừng nói phải giữ toàn thây arr gốc|luật rừng nói phải giữ toàn thây arr gốc|luật rừng nói phải giữ toàn thây arr gốc|luật rừng nói phải giữ toàn thây arr gốc|

Vì thằng for of có thể làm mọi thứ, bạn tự hỏi sao không dùng for of luôn, đa năng thế cơ mà, sinh ra mấy thằng map, filter, reduce... kia làm gì?

Vấn đề chính là ở đó, vì nó làm được mọi thứ nên khi nhìn vào sẽ không biết nó định làm gì.

Còn mấy thằng kia, mỗi thằng một việc, nhìn vào là biết ngay nó định làm gì.

thằng for of giống như 1 con dao đa năng, khi bạn cầm nó trong tay thì bố mẹ bạn không hiểu là hôm nay trời nắng bạn hứng lên đi nam công gia chánh, nấu ăn cắm hoa, cắt ghép dây điện, hay là bạn muốn xiên họ.

mấy thằng map, filter, reduce... thì như 1 con dao bé xíu gọt hoa quả, 1 cái kéo cắt tỉa hoa, một cái kìm cắt dây diện, vì bản thân nó chỉ làm được 1 việc nên khi bạn dùng nó thì người khác đỡ phải hoài nghi xem rốt cục bạn muốn làm cái gì.

Điều này giúp code trở nên dễ hiểu.

OK vậy thống nhất là hãy dùng map, filter, reduce, chỉ dùng đến anh for of nói riêng và anh for nói chung khi mấy thằng kia không làm được việc thôi nhé!

dùng map khi muốn tạo một array mới với chiều dài bằng chính xác array cũ, với các thành viên là từ array cũ mà "chế" ra. Không thay đổi array cũ.

Gọi map là 1 nhà máy
alt text

giả sử mình có một binh đoàn chuyên lau chùi nhà cửa cho mình, gồm các thành viên sau

alt text

Bạn thấy binh đoàn này rất được, nên ngỏ ý mượn về để clone lại có chỉnh sửa, ví dụ nối thêm tai thỏ chẳng hạn.
alt text

Khi đó việc của bạn chỉ là

  • tìm đến nhà máy map
  • nói cho map biết tôi muốn nối tai thỏ cho đàn mèo

còn lại moi việc cứ để map lo, map sẽ:

  • clone mèo đầu tiên, tạo ra sản phẩm gọi là mèoclone1
  • nối tai thỏ cho mèoclone1
  • đẩy mèoclone1 vào binh đoàn của bạn
  • clone mèo thứ hai, tạo ra sản phẩm gọi là mèoclone2
  • nối tai thỏ cho mèoclone2
  • đẩy mèoclone2 vào binh đoàn của bạn .... cứ thế đến hết đàn mèo
  • trả cho bạn đàn mèo tai thỏ
  • trả cho mình đàn mèo nguyên gốc của mình

vậy là clone trước, chỉnh sau.

map array cũng là clone xong rồi chỉnh chứ không được chỉnh xong rồi clone. vậy tan nát mất array gốc.

ví dụ sau đây về map một array, bốc từng thằng, nhân đôi lên, đẩy vào arr2, mọi việc đều được map âm thầm làm, bạn không phải làm gì cả ngoài việc chuẩn bị một callback function để giải thích xem bạn muốn chỉnh sửa từng thằng như nào (trong ví dụ này là nhân đôi nó)

let arr1 = [1,2,4];
let arr2 = arr1.map(x=>x*2);
console.log(arr2);   [2, 4, 8]
console.log(arr1); [1, 2, 4]

method ở đây là map, callback function ở đây là (x=>x*2)

alt text

Bạn sẽ phải tìm đến nhà máy filter khi bạn thích đàn mèo của mình nhưng chỉ muốn clone không chỉnh sửa một vài chú mèo trong binh đoàn chứ không phải tất cả

ví dụ chỉ chọn chú nào có lông pha màu xám chẳng hạn, ngoài ra thì không cần ghép tai hay gì cả mà thấy đẹp lắm rồi
alt text

Tương tự nhà máy map, nhà máy filter chỉ cần bạn nói là bạn muốn chọn mèo như thế nào thì đạt yêu cầu còn lại mọi việc filter sẽ lo.

Sau đây là 1 ví dụ

let arr1 = [1,2,4];
let arr2 = arr1.filter(x=>x>3);
console.log(arr2);   [4]
console.log(arr1);   [1, 2, 4]

ở ví dụ trên, điều kiện trúng tuyển của x là phải lớn hơn 3, vậy nên trong ba con số là 1, 2, 4 chỉ có mình con 4 trúng tuyển.

method ở đây là filter, callback function ở đây là x=>x>3

Nhắc lại, x là từng thằng trong array do ta đưa vào nhà máy. Đây chỉ là cách gọi, bạn thích gọi là y là z hay là conchuot đều được. Miễn là trước dấu mũi tên => gọi là gì thì sau mũi tên phải gọi là như vậy. Chứ không được kiểu râu ông nọ cắm cằm bà kia như này x=>conchuot>3

alt text
Lưu ý nếu không nhập con số khởi điểm thì mặc định con số khởi điểm bằng với thằng đầu tiên trong arr1

chúng ta có thể tưởng tượng reduce này là một con rắn săn mồi, nó sẽ lê la đến array của bạn và đớp từng thành viên một, sau mỗi lần đớp nó sẽ dài ra một đoạn bằng đúng độ dài của thành viên đó, và cuối cùng con rắn đi ra với độ dài siêu khủng, đó là giá trị return.
alt text

con số khởi điểm ở đây chính là chiều dài ban đầu của rắn. Nếu bạn không nhập chiều dài ban đầu của rắn thì mặc định rắn có chiều dài là thành viên đầu tiên, và rắn sẽ bắt đầu đớp từ thành viên thứ hai trở đi (bỏ qua thành viên 1)

có ví dụ sau

let arr1 = [3,1,7];
let conRanDiRa = arr1.reduce((chieuDaiRanHienTai,x)=>chieuDaiRanHienTai+x);
console.log(conRanDira);   11

x ở đây vẫn đại diện lần lượt cho từng thành viên trong array nhé.

không cho chiều dài rắn ban đầu nên mặc định sẽ bằng thành viên đầu tiên, tức bằng 3

rắn bắt đầu đớp từ thành viên thứ hai trở đi

ban đầu rắn dài 3 mét, đớp xong thành viên thứ hai thì rắn có chieuDaiRanHienTai là bằng 3+1=4

đớp xong thành viên cuối bằng 7 thì rắn có chieuDaiRanHienTai bằng 4+7=11

cuối cùng thì rắn ta đi ra với chiều dài siêu khủng là 11 mét.

Vậy cữ mỗi lần đớp thì chieuDaiRanHienTai lại thay đổi nhưng đó là việc của reduce, ta chỉ cần biết reduce nó đang làm gì nhưng không phải lo, không cần quan tâm đến cái chieuDaiRanHienTai đó, ta chỉ quan tâm đến kết quả là con rắn đi ra khỏi array và dài chốt lại là bao nhiêu.

Nhiệm vụ của ta chỉ là nói cho reduce biết chiều dài ban đầu của rắn (nếu muốn) và muốn reduce làm gì với mỗi lần đớp.

Sau đây là ví dụ mà có nhập chiều dài ban đầu của rắn

let arr1 = [3,1,7];
let conRanDiRa = arr1.reduce((chieuDaiRanHienTai,x)=>x+chieuDaiRanHienTai,1000);
console.log(conRanDiRa);   1011

Như các bạn đã thấy, ta muốn nhập chiều dài ban đầu của rắn thì chỉ việc thêm con số đó vào cuối method reduce.

Ở đây mình cho chiều dài ban đầu là 1000 mét (siêu rắn cuồng nộ). Nó ăn thịt từng thành viên của array rồi đi ra với chiều dài là 1011 mét, có vẻ không xi nhê gì vì vốn đã to rồi :smile:

Tuy nhiên reduce còn làm được nhiều thứ, như ở trên có nhắc tới đoạn muốn reduce làm gì với mỗi lần đớp. Làm gì ở đây là làm gì?

let arr1 = [3,1,7];
let conRanDiRa = arr1.reduce((chieuDaiRanHienTai,x)=>x*10+chieuDaiRanHienTai,0);
console.log(conRanDiRa);  110

Ở đây reduce đã làm một siêu năng lực đó là với mỗi thức ăn đưa cho rắn sẽ đem lại hiệu quả tăng trưởng gấp 10, do đó rắn ăn 1 con vịt nhưng hấp thụ vào người 10 con vịt.

Ban đầu rắn có chiều dài bằng 0 mét (hơi vô lý nhưng cả câu chuyện này có chỗ nào có lý đâu haha :smile: tạm chấp nhận cho đơn giản nha). Sau khi đớp xong thằng thứ nhất của array (3) nó tăng trưởng thêm 30 mét và có chieuDaiRanHienTai là 30m. đớp thêm thằng thứ hai (1) nó tăng thêm 10met và cuối cùng là 70m. Cuối cùng rắn ta chui ra với chiều dài 30 + 10 +70 = 110m.

Ví dụ này mình cho siêu năng lực gấp 10 nhưng trong thực tế bạn thích làm gì thì làm nhé, cộng trừ nhân chia lấy phần dư làm tròn căn bậc...bla bla. Miễn trong function đừng có phá nát cái array cũ (bằng hành vi gán) thì thích làm gì cũng chiều :smile:

Mọt hình ảnh mnh họa cho reduce:
alt text

Zậy là chúng ta đã tiêu hóa xong 3 method khó tiêu là map, filter, reduce. Không biết còn ai chưa tiêu được thì cứ mạnh dạn liên hệ để mua Béc bê rin nhé! <3

0