Nếu bạn mới bắt đầu code JavaScript trong một thời gian ngắn, có thể bạn chỉ mới nghe đến .map()
, .reduce()
và .filter()
chứ ko thực sự dùng nó nhiều. Đối với mình, phải mất một thời gian vì mình code web cho cty Nhật. Nên phải code làm sao để có thể hỗ trợ Internet Explorer 8 và một số trình duyệt cũ hơn tý (Mà chủ yếu án maintenance thôi còn lại mới thì đa phân ES6 hết). Vì thế, nếu bạn không cần code ra một cái gì đó mà nó cần tương thích với mấy cái trình duyệt rất cũ này, thì bạn phải làm quen với các hàm như .map()
, .reduce()
và .filter()
nó sẽ giụp bạn rất nhiều.
Ví dụ mở đầu: cái hàm groupBy này chỉ vài dòng đơn giản là đã có thể vừa group + filter quá tiện
Nếu lướt qua hết bài này mình nghĩ bạn cũng hoàn toàn có thể code ra một hàm y như vậy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | <span class="token keyword">const</span> personnel <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Luke Skywalker"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">98</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">56</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">82</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Sabine Wren"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">73</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">99</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">22</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Zeb Orellios"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">59</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">15</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Ezra Bridger"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">43</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">67</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">11</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Caleb Dume"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">71</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">85</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">/** * Hàm GroupBy theo hàm điều kiện * @param fnKey Điều kiện group by * @param fnValue Điều kiện filter cho mỗi kết quả * @param list List đầu vào * @returns Trả về 1 đối tượng */</span> <span class="token keyword">function</span> <span class="token function">groupBy</span><span class="token punctuation">(</span>list<span class="token punctuation">,</span> fnKey<span class="token punctuation">,</span> <span class="token function-variable function">fnValue</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> list<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span> <span class="token punctuation">(</span><span class="token parameter">prev<span class="token punctuation">,</span> next</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>prev<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token function">fnKey</span><span class="token punctuation">(</span>next<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">(</span>prev<span class="token punctuation">[</span><span class="token function">fnKey</span><span class="token punctuation">(</span>next<span class="token punctuation">)</span><span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">fnValue</span><span class="token punctuation">(</span>next<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> <span class="token function">groupBy</span><span class="token punctuation">(</span> personnel<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>isForceUser <span class="token operator">?</span> <span class="token string">"Force User"</span> <span class="token operator">:</span> <span class="token string">"Not Force User"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> e<span class="token punctuation">.</span>name <span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Kết quả: Điểm đặc biết nữa là độ khó thuật toán này là (On)
quá ổn
1 2 3 4 5 | <span class="token punctuation">{</span> <span class="token string">'Force User'</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'Luke Skywalker'</span><span class="token punctuation">,</span> <span class="token string">'Ezra Bridger'</span><span class="token punctuation">,</span> <span class="token string">'Caleb Dume'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">'Not Force User'</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'Sabine Wren'</span><span class="token punctuation">,</span> <span class="token string">'Zeb Orellios'</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> |
Hãy lưu ý rằng bài viết này rất có thể sẽ áp dụng được cho bất kỳ ngôn ngữ lập trình nào khác mà bạn có thể đang sử dụng, vì đây là những khái niệm tồn tại trong nhiều ngôn ngữ khác.
OK GÉT GÔ
.map()
Hãy để mình giải thích cách nó hoạt động với một ví dụ đơn giản. Giả sử bạn đã nhận được một array chứa nhiều đối tượng – mỗi đối tượng đại diện cho một người. Tuy nhiên, điều bạn thực sự cần cuối cùng là một array chỉ chứa id của mỗi người.
1 2 3 4 5 6 7 8 9 10 | <span class="token comment">// Những gì bạn có </span> <span class="token keyword">var</span> officers <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">'Captain Piett'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">24</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">'General Veers'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">56</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">'Admiral Ozzel'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">88</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">'Commander Jerjerrod'</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Thứ bạn cần </span> <span class="token punctuation">[</span><span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">24</span><span class="token punctuation">,</span> <span class="token number">56</span><span class="token punctuation">,</span> <span class="token number">88</span><span class="token punctuation">]</span> |
Có nhiều cách để đạt được điều này. Bạn có thể muốn làm điều đó bằng cách tạo một array trống, sau đó sử dụng .forEach()
, .for(...of)
hoặc đơn giản .for()
để đạt được mục đích của mình.
Hãy so sánh các cách làm đó nhé!
Sử dụng .forEach()
:
1 2 3 4 5 | <span class="token keyword">var</span> officersIds <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> officers<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">officer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> officersIds<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>officer<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Lưu ý theo cách này bạn phải tạo một array trống trước?
Tiếp theo, hãy xem nó trông như thế nào khi sử dụng .map()
:
1 2 3 4 5 | <span class="token keyword">var</span> officersIds <span class="token operator">=</span> officers<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">officer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> officer<span class="token punctuation">.</span>id <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Chúng ta thậm chí có thể ngắn gọn hơn với các arrow functions (Lưu ý: ES6, Babel hoặc TypeScript)
1 2 | <span class="token keyword">const</span> officersIds <span class="token operator">=</span> officers<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">officer</span> <span class="token operator">=></span> officer<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Vậy làm thế nào để .map()
làm việc? Về cơ bản là có 2 đối số, một hàm callback và một contexts tùy chọn (mặc định sẽ là this
trong hàm callback) mà mình đã không sử dụng trong ví dụ trước. Hàm callback sẽ được call cho từng value trong array và trả về từng value mới trong array kết quả.
Hãy nhớ rằng array kết quả sẽ luôn có cùng độ dài với array ban đầu.
.reduce()
Giống như .map()
, .reduce()
cũng gọi hàm callback cho từng phần tử của array. Điều khác biệt ở đây là reduce
chuyển kết quả của hàm callback này cho một bộ tích lũy
từ phần tử array này sang phần tử array khác.
Bộ tích lũy có thể là bất kỳ thứ gì (số nguyên, chuỗi, đối tượng, v.v.) và phải được khởi tạo hoặc truyền vào khi gọi .reduce()
.
Đến lúc làm vài ví dụ để giễ hiểu hơn nào!
Giả sử bạn có một list phi công và số năm kinh nghiệm tương ứng của họ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">var</span> pilots <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">10</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Poe Dameron"</span><span class="token punctuation">,</span> years<span class="token operator">:</span> <span class="token number">14</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Temmin 'Snap' Wexley"</span><span class="token punctuation">,</span> years<span class="token operator">:</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">41</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Tallissan Lintra"</span><span class="token punctuation">,</span> years<span class="token operator">:</span> <span class="token number">16</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">99</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Ello Asty"</span><span class="token punctuation">,</span> years<span class="token operator">:</span> <span class="token number">22</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> |
Chúng ta cần biết tổng số năm kinh nghiệm của tất cả họ. Với .reduce()
, nó khá đơn giản:
1 2 3 4 | <span class="token keyword">var</span> totalYears <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">accumulator<span class="token punctuation">,</span> pilot</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> accumulator <span class="token operator">+</span> pilot<span class="token punctuation">.</span>years<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Lưu ý rằng mình đã đặt value
bắt đầu là 0
. Mình cũng có thể sử dụng một object nếu cần. Sau khi gọi callback cho từng phần tử của array, reduce sẽ trả về value cuối cùng của bộ tích lũy
của chúng ta (trong trường hợp này là: 82
).
Và tất nhiên nó cũng có thể được rút ngắn với các arrow functions của ES6:
1 2 | <span class="token keyword">const</span> totalYears <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> pilot</span><span class="token punctuation">)</span> <span class="token operator">=></span> acc <span class="token operator">+</span> pilot<span class="token punctuation">.</span>years<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Bây giờ, giả sử mình muốn tìm phi công nào là người có kinh nghiệm nhất. Đối với câu hỏi này, mình cũng có thể sử dụng reduce
:
1 2 3 4 | <span class="token keyword">var</span> mostExpPilot <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">oldest<span class="token punctuation">,</span> pilot</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">(</span>oldest<span class="token punctuation">.</span>years <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">></span> pilot<span class="token punctuation">.</span>years <span class="token operator">?</span> oldest <span class="token operator">:</span> pilot<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Mình đặt tên cho bộ tích lũy
của mình là oldest
. Hàm callback của mình so sánh bộ tích lũy với từng phi công. Nếu một phi công có nhiều năm kinh nghiệm hơn oldest
, thì phi công đó sẽ trở thành phi công oldest
đến cuối cùng mình sẽ trả về oldest
.
Như bạn có thể thấy, sử dụng .reduce()
là một cách dễ dàng để tạo một value
hoặc một Object
từ một array.
.filter()
Nếu bạn có một array, nhưng chỉ muốn một số phần tử trong đó thì sao? Đó là khi bạn cần dùng tới .filter()
Đây là dữ liệu của chúng ta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">var</span> pilots <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Wedge Antilles"</span><span class="token punctuation">,</span> faction<span class="token operator">:</span> <span class="token string">"Rebels"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">8</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Ciena Ree"</span><span class="token punctuation">,</span> faction<span class="token operator">:</span> <span class="token string">"Empire"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">40</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Iden Versio"</span><span class="token punctuation">,</span> faction<span class="token operator">:</span> <span class="token string">"Empire"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">66</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Thane Kyrell"</span><span class="token punctuation">,</span> faction<span class="token operator">:</span> <span class="token string">"Rebels"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
Giả sử bây giờ chúng ta muốn có hai array: một array dành cho quân “Rebels”, array còn lại dành cho quân “Empire”. Với .filter()
nó vô dùng đơn giản!
1 2 3 4 5 6 7 | <span class="token keyword">var</span> rebels <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">pilot</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> pilot<span class="token punctuation">.</span>faction <span class="token operator">===</span> <span class="token string">"Rebels"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">var</span> empire <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">pilot</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> pilot<span class="token punctuation">.</span>faction <span class="token operator">===</span> <span class="token string">"Empire"</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Và nó thậm chí còn ngắn hơn với các arrow functions:
1 2 3 | <span class="token keyword">const</span> rebels <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">pilot</span> <span class="token operator">=></span> pilot<span class="token punctuation">.</span>faction <span class="token operator">===</span> <span class="token string">"Rebels"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> empire <span class="token operator">=</span> pilots<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">pilot</span> <span class="token operator">=></span> pilot<span class="token punctuation">.</span>faction <span class="token operator">===</span> <span class="token string">"Empire"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Về cơ bản, nếu function callback trả về là true, phần tử hiện tại sẽ nằm trong array kết quả. Nếu nó trả về false
, nó sẽ không được đưa vào danh sách kết quả.
Đến phần hay kết hợp .map(), .reduce() và .filter()
Vì cả ba đều được call trên các array và vì .map()
và .filter()
cả hai đều trả về array, nên chúng ta có thể dễ dàng xâu chuỗi các lệnh gọi của mình.
Hãy xem một ví dụ khác. Đây là dữ liệu của chúng ta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | <span class="token keyword">var</span> personnel <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Luke Skywalker"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">98</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">56</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">82</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Sabine Wren"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">73</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">99</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">22</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Zeb Orellios"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">20</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">59</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">15</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Ezra Bridger"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">43</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">67</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">11</span><span class="token punctuation">,</span> name<span class="token operator">:</span> <span class="token string">"Caleb Dume"</span><span class="token punctuation">,</span> pilotingScore<span class="token operator">:</span> <span class="token number">71</span><span class="token punctuation">,</span> shootingScore<span class="token operator">:</span> <span class="token number">85</span><span class="token punctuation">,</span> isForceUser<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
Mục tiêu của chúng ta là: chỉ cần có được tổng số điểm của những user có isForceUser
là true
. Hãy làm điều đó từng bước một!
Đầu tiên, chúng ta cần lọc ra những user
mà isForceUser
là false
:
1 2 3 4 5 | <span class="token keyword">var</span> jediPersonnel <span class="token operator">=</span> personnel<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">person</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> person<span class="token punctuation">.</span>isForceUser<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Result: [{...}, {...}, {...}] (Luke, Ezra and Caleb)</span> |
Sau khi lọc, chúng ta có 3 phần tử còn lại trong array kết quả của mình. Bây giờ chúng ta cần tạo một array chứa tổng số điểm của mỗi Jedi
.
1 2 3 4 5 | <span class="token keyword">var</span> jediScores <span class="token operator">=</span> jediPersonnel<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">jedi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> jedi<span class="token punctuation">.</span>pilotingScore <span class="token operator">+</span> jedi<span class="token punctuation">.</span>shootingScore<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Result: [154, 110, 156]</span> |
Và hãy sử dụng reduce
để có được tổng số:
1 2 3 4 5 | <span class="token keyword">var</span> totalJediScore <span class="token operator">=</span> jediScores<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> score</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> acc <span class="token operator">+</span> score<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Result: 420</span> |
Và bây giờ là phần thú vị… chúng ta có thể xâu chuỗi tất cả những thứ này để có được thứ chúng ta muốn trong một dòng duy nhất:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">var</span> totalJediScore <span class="token operator">=</span> personnel <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">person</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> person<span class="token punctuation">.</span>isForceUser<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">jedi</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> jedi<span class="token punctuation">.</span>pilotingScore <span class="token operator">+</span> jedi<span class="token punctuation">.</span>shootingScore<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> score</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> acc <span class="token operator">+</span> score<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Và hãy xem nó ngắn gọn như thế nào nếu sử dụng với arrow functions:
1 2 3 4 5 | <span class="token keyword">const</span> totalJediScore <span class="token operator">=</span> personnel <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token parameter">person</span> <span class="token operator">=></span> person<span class="token punctuation">.</span>isForceUser<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">jedi</span> <span class="token operator">=></span> jedi<span class="token punctuation">.</span>pilotingScore <span class="token operator">+</span> jedi<span class="token punctuation">.</span>shootingScore<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> score</span><span class="token punctuation">)</span> <span class="token operator">=></span> acc <span class="token operator">+</span> score<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Bùm! Quá ngầu đúng ko nhìn ngắn gọn giễ hiểu hơn nhiều nhỉ
Lưu ý: Trong ví dụ trên, .map()
và .filter()
thậm chí còn không cần thiết. Chúng ta có thể dễ dàng đạt được kết quả tương tự chỉ với .reduce()
. Tuy nhiên ở ví dụ trên mình vẫn dùng nó để các bạn giễ hình dung hơn. Bạn có thể đoán xem làm cách nào chỉ cần dùng .reduce()
mà vẫn nhận được kết quả tương tự chỉ với với một dòng code không?
Ten tèn easy đúng đúng ko:
1 2 3 4 5 6 7 8 | <span class="token keyword">const</span> totalJediScore <span class="token operator">=</span> personnel<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span> <span class="token punctuation">(</span><span class="token parameter">acc<span class="token punctuation">,</span> person</span><span class="token punctuation">)</span> <span class="token operator">=></span> person<span class="token punctuation">.</span>isForceUser <span class="token operator">?</span> acc <span class="token operator">+</span> person<span class="token punctuation">.</span>pilotingScore <span class="token operator">+</span> person<span class="token punctuation">.</span>shootingScore <span class="token operator">:</span> acc<span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> |
Tại sao không sử dụng .forEach()?
Câu hỏi rất hay, thực ra thì mình đã từng sử dụng các vòng lặp for
ở mọi nơi thay vì .map()
, .reduce()
và .filter()
. Nhưng gần đây, khi có cơ hội được tự tay viết những function base cho dự án hiện tại của cty, mình đã bắt đầu làm việc nhiều hơn với dữ liệu đến từ API. Đó là lúc mình bắt đầu thấy những lợi thế của việc sử dụng chúng. Như bạn thấy ví dụ đầu tiên mình có đưa ra bạn hoàn toàn có thể giải quyết một bài toán chỉ với 1 dòng code.
Formatting
Giả sử bạn có 1 danh sách chứa name
và title
:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">var</span> data <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">"Jan Dodonna"</span><span class="token punctuation">,</span> title<span class="token operator">:</span> <span class="token string">"General"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">"Gial Ackbar"</span><span class="token punctuation">,</span> title<span class="token operator">:</span> <span class="token string">"Admiral"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">]</span> |
API cung cấp cho bạn dữ liệu trên, nhưng bạn chỉ cần phần tiêu đề và họ của từng người… Vì ta có fullname nên chắc chắn phải định dạng nó lại đúng ko? (firstname + lastname)
Vì vậy bạn phải viết một function định dạng dữ liệu để sử dụng mỗi khi chúng ta lặp.
Điều đó có nghĩa là bạn không thể có vòng lặp .forEach
bên trong hàm formatElement
, nếu không bạn sẽ phải bọc phần tử đơn lẻ của mình trong một array trước khi chuyển nó vào hàm chỉ để nó hoạt động, như sau:
1 2 3 | <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">formatElement</span><span class="token punctuation">(</span><span class="token punctuation">[</span>element<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Yeah... điều đó không đúng chút nào</span> |
Vì vậy, vòng lặp của bạn phải kết thúc việc gọi của hàm, như thế này:
1 2 3 4 5 | data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> formatted <span class="token operator">=</span> <span class="token function">formatElement</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Nhưng sau đó thì sao.... </span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Nhưng .forEach()
không trả lại bất cứ điều gì. Điều đó có nghĩa là bạn phải đẩy kết quả vào bên trong một array được khai báo từ trước.
1 2 3 4 5 6 | <span class="token keyword">var</span> results <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">element</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> formatted <span class="token operator">=</span> <span class="token function">formatElement</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span> results<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>formatted<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Kết quả là bạn có 2 hàm: hàm formatElement()
và hàm push
kết quả vào array của bạn.
Tại sao có 2 function khi bạn chỉ cần 1 là đủ:
1 2 | <span class="token keyword">var</span> results <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>formatElement<span class="token punctuation">)</span><span class="token punctuation">;</span> |
Testing dễ dàng hơn
Nếu bạn viết các Unit tests cho code của mình, bạn sẽ thấy việc kiểm tra các function bằng .map()
, .reduce()
hoặc .filter()
, đơn giản hơn.
Tất cả những gì bạn phải làm là cung cấp dữ liệu cho hàm và mong đợi kết quả xuất hiện. Về cơ bản “điều gì sẽ xảy ra nếu điều này được passed
?”. Ít thao tác hơn, ít beforeEach()
và afterEach()
hơn. Nó sẽ đơn giản hơn rất nhiều.
Cố gắng thay thế một số vòng lặp for
của bạn bằng .map()
, .reduce()
, .filter()
nơi nó có vẻ phù hợp. Mình đảm bảo code của bạn sẽ bớt lộn xộn và dễ đọc hơn nhiều.
Ngày xưa khi mình mới sử dụng mấy hàm này đặc biệt là .reduce()
mình cũng rất bối rối nhưng dần dần rồi sẽ quen ấy mà.
Roundup
Như mọi khi, mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.
Cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo!
Nếu bạn thấy Blog này hay xin hãy cho mình một like và đăng ký để ủng hộ mình nhé. Thank you.