<
>
ETHIOCODE
SOFTSELECT
แˆถแแ‰ตแ‹Œแˆญ แˆแˆญแŒซ แˆˆแŠขแ‰ตแ‹ฎแŒตแ‹ซ
P3 ยท L07 ยท LODASH ยท UTILITY FUNCTIONS ยท LOADING...
PHASE 3 ยท LESSON 07

Lodash โ€”
แ‹จแ‹ดแ‰จแˆŽแแˆฎแ‰ฝ แ‹จแŒ€แˆญแ‰ฃ แŠ แŒฅแŠ•แ‰ต! ๐Ÿ”ง

Array แฃ Object แฃ String โ€” แˆแˆ‰แŠ•แˆ แ‰ แ‰€แˆ‹แˆ‰ แˆˆแˆ›แˆตแ‰ฐแ‹ณแ‹ฐแˆญ (manipulate แˆˆแˆ›แ‹ตแˆจแŒ)! JavaScript แˆ‹แ‹ญ แ‹จแŒŽแ‹ฐแˆ‰แ‰ตแŠ• แАแŒˆแˆฎแ‰ฝ แ‰ แˆ™แˆ‰ แ‹ซแˆŸแˆ‹แˆ!
debounce ยท throttle ยท cloneDeep ยท groupBy ยท merge โ€” แˆแˆ‰แˆ แˆˆ Production แ‹แŒแŒ แŠ“แ‰ธแ‹! โšก
A modern JavaScript utility library delivering modularity, performance & extras.

แ‹ซแ‹˜แŒ‹แŒ€แ‹: @AppMinds_ET
01Lodash แˆแŠ•แ‹ตแŠ• แАแ‹? โ€” แˆˆแˆแŠ• แ‹ซแˆตแˆแˆแŒˆแŠ“แˆ?

Lodash (_) โ€” แ‰  JavaScript แ‹แˆตแŒฅ แ‹จแŒŽแ‹ฐแˆ‰แ‰ตแŠ• แŒ แ‰ƒแˆš แˆแŠ•แŠญแˆฝแŠ–แ‰ฝ (utility functions) แ‰ แˆ™แˆ‰ แ‹จแ‹ซแ‹˜ แ‰คแ‰ฐ-แˆ˜แŒฝแˆแแ‰ต (library) แАแ‹! Array แฃ Object แฃ String แฃ Function manipulation โ€” แ‰€แˆˆแˆ แ‹ซแˆˆ แŠฅแŠ“ แˆตแˆ…แ‰ฐแ‰ต แ‹จแˆŒแˆˆแ‹ แŠฎแ‹ต แŠฅแŠ•แ‹ตแŠ•แ…แ แ‹ญแˆจแ‹ณแŠ“แˆ! Tree-shakeable แˆตแˆˆแˆ†แА โ€” แ‹จแˆแŠ•แˆแˆแŒˆแ‹แŠ• function แ‰ฅแ‰ป แˆˆแ‹ญแ‰ฐแŠ• import แˆ›แ‹ตแˆจแŒ แŠฅแŠ•แ‰ฝแˆ‹แˆˆแŠ•!
Used by millions โ€” React, Angular, Vue, Node.js โ€” everywhere!

๐Ÿ“ฆ

npm install lodash && npm install @types/lodash
import _ from 'lodash'  |  import { debounce } from 'lodash' (tree-shake!)

โŒ แ‹ซแˆˆ Lodash โ€” แ‰ฅแ‹™ แŠฎแ‹ต + แˆตแˆ…แ‰ฐแ‰ต แ‹จแˆ˜แแŒ แˆญ แ‹•แ‹ตแˆ
// Deep clone แˆˆแˆ›แ‹ตแˆจแŒ?
JSON.parse(JSON.stringify(obj))
// โŒ Dates แฃ undefined แฃ Functions แ‹ญแ‰ แˆ‹แˆปแˆ‰!

// ArrayแŠ• แ‰ แˆแ‹ตแ‰ฅ แˆˆแˆ˜แŠจแ‹แˆแˆ (Group)?
arr.reduce((acc, item) => {
  acc[item.cat] = acc[item.cat] || [];
  acc[item.cat].push(item);
  return acc;
}, {}) // โŒ 4 แˆ˜แˆตแˆ˜แˆญ แŠฎแ‹ต แˆˆ 1 แˆตแˆซ!

// Debounce แˆˆแˆ›แ‹ตแˆจแŒ?
let timer;
clearTimeout(timer);
timer = setTimeout(fn, 300);
โœ… แ‰  Lodash โ€” 1 แˆ˜แˆตแˆ˜แˆญ แ‰ฅแ‰ป!
import _ from 'lodash';

// โœ… Deep clone โ€” แˆˆแˆแˆ‰แˆ แŠ แ‹ญแАแ‰ต แ‹ณแ‰ณ (type) แ‹ญแˆฐแˆซแˆ!
_.cloneDeep(obj)

// โœ… Group โ€” แ‰  1 แˆ˜แˆตแˆ˜แˆญ แ‰ฅแ‰ป!
_.groupBy(arr, 'category')

// โœ… Debounce โ€” แˆˆ production แ‹แŒแŒ แ‹จแˆ†แА!
_.debounce(fn, 300)
02๐Ÿ” Function Explorer โ€” แˆแˆ‰แŠ•แˆ Function แˆžแŠญแˆฉ!
๐Ÿ”ง Lodash Function แˆ˜แˆ˜แˆแŠจแ‰ป โ€” Function แŒ แ‰… แ‹ซแ‹ตแˆญแŒ‰ โ†’ แˆ›แ‰ฅแˆซแˆชแ‹ซ + แŠฎแ‹ต แ‹ซแŒˆแŠ›แˆ‰
INTERACTIVE
๐Ÿ‘ˆ
Function แ‹ญแˆแˆจแŒก
แ‰ แŒแˆซ แ‰ แŠฉแˆ แŠซแˆˆแ‹ แ‹แˆญแ‹แˆญ แ‹แˆตแŒฅ function แŒ แ‰… แ‹ซแ‹ตแˆญแŒ‰ โ†’ แˆ›แ‰ฅแˆซแˆชแ‹ซแ‹ แ‹ญแ‰ณแ‹ซแˆ!
03๐Ÿ“š Categories โ€” Array ยท Object ยท String ยท Function
lodash-categories.ts
CODE REFERENCE
๐Ÿ“ฆ Array
๐Ÿ—‚ Object
๐Ÿ”ค String
โšก Function
๐Ÿ”— Collection
array-methods.ts
import { chunk, flatten, uniq, difference,
         intersection, compact, zip, unzip,
         groupBy, sortBy, orderBy } from 'lodash';

const arr = [1,2,3,4,5,6,7];
const nums = [1,2,2,3,3,4];
const nested = [[1,2],[3,4],[5]];

// chunk โ€” แ‰ตแˆแ‰… arrayแŠ• แ‹ˆแ‹ฐ แ‰ตแŠ“แŠ•แˆฝ chunks (แŠญแแˆŽแ‰ฝ) แˆˆแˆ˜แŠจแ‹แˆแˆ
chunk(arr, 3)        // [[1,2,3],[4,5,6],[7]]

// flatten โ€” แ‹จแ‰ฐแ‹ฐแˆซแˆจแ‰  (nested) arrayแŠ• แ‰€แŒฅ แˆˆแˆ›แ‹ตแˆจแŒ
flatten(nested)      // [1,2,3,4,5]
flattenDeep([[1,[2]]])  // [1,2] (แˆ™แˆ‰ แ‰ แˆ™แˆ‰ แ‰€แŒฅ แ‹ซแ‹ฐแˆญแŒˆแ‹‹แˆ!)

// uniq โ€” แ‹จแ‰ฐแ‹ฐแŒ‹แŒˆแˆ™ (duplicates) แ‹ณแ‰ณแ‹Žแ‰ฝแŠ• แˆˆแˆ›แˆตแ‹ˆแŒˆแ‹ต
uniq(nums)           // [1,2,3,4]
uniqBy(users, 'id') // แ‰ แ‰ฐแ‹ˆแˆฐแА property แ‰ฅแ‰ป แАแŒฅแˆŽ แˆˆแˆ›แ‹แŒฃแ‰ต

// Set operations (แ‹จแˆตแ‰ฅแˆตแ‰ฅ แˆตแˆŒแ‰ถแ‰ฝ)
difference([1,2,3,4],[2,4])   // [1,3]
intersection([1,2],[2,3]) // [2]
union([1,2],[2,3])       // [1,2,3]

// compact โ€” falsy แ‹จแˆ†แŠ‘ แŠฅแˆดแ‰ถแ‰ฝแŠ• แˆˆแˆ›แˆตแ‹ˆแŒˆแ‹ต
compact([0,1,false,2,'',3]) // [1,2,3]

// zip / unzip
zip(['a','b'],[1,2]) // [['a',1],['b',2]]
object-methods.ts
import { cloneDeep, merge, pick, omit,
         get, set, has, mapValues,
         invert, defaults } from 'lodash';

const user = { name:'Samuel', age:26,
               addr:{ city:'Addis', zip:'1000' } };

// cloneDeep โ€” แˆ™แˆ‰ (Deep) Clone แˆˆแˆ›แ‹ตแˆจแŒ
const clone = cloneDeep(user);
clone.addr.city = 'Hawassa'; // แ‹‹แŠ“แ‹ (original) แ‹ณแ‰ณ แŠ แ‹ญแ‰€แ‹จแˆญแˆ!

// merge โ€” objects แˆ›แ‹‹แˆƒแ‹ต (แ‰ แŒฅแˆแ‰€แ‰ต / deep)
merge({a:1,b:{x:1}}, {b:{y:2},c:3})
// โ†’ {a:1, b:{x:1,y:2}, c:3}

// pick / omit โ€” แ‹จแ‰ฐแ‹ˆแˆฐแŠ‘ fields แ‰ฅแ‰ป แˆˆแˆ˜แˆแˆจแŒฅ
pick(user, ['name','age'])   // {name,age} แ‰ฅแ‰ป แ‹ญแˆฐแŒ แŠ“แˆ
omit(user, ['addr'])        // 'addr'แŠ• แ‰ตแ‰ถ แˆŒแˆ‹แ‹แŠ• แ‹ญแˆฐแŒ แŠ“แˆ

// get โ€” safe nested access (แ‹ซแˆˆ แˆตแˆ…แ‰ฐแ‰ต แ‹ณแ‰ณ แˆˆแˆ›แ‹แŒฃแ‰ต)
get(user, 'addr.city')      // 'Addis'
get(user, 'addr.phone', 'N/A') // แŠจแˆŒแˆˆ 'N/A' (default) แ‹ญแˆ˜แˆแˆณแˆ
has(user, 'addr.zip')       // true

// set โ€” nested value แˆˆแˆ›แˆตแ‰€แˆ˜แŒฅ
set(clone, 'addr.phone', '0911')

// mapValues โ€” แ‰  object แ‹แˆตแŒฅ แ‹ซแˆ‰แ‰ตแŠ• แŠฅแˆดแ‰ถแ‰ฝ (values) แ‰ แˆ™แˆ‰ แˆˆแˆ˜แ‰€แ‹จแˆญ
mapValues({a:1,b:2}, v => v*2)
// โ†’ {a:2, b:4}
string-methods.ts
import { camelCase, snakeCase, kebabCase,
         capitalize, startCase, truncate,
         pad, padStart, padEnd, trim,
         escape, unescape, words } from 'lodash';

const str = 'hello world ethiopia';

// ๐Ÿช Case conversion (แ‹จแŠ แƒแƒแ แˆตแˆแ‰ต แˆ˜แ‰€แ‹จแˆชแ‹ซ)
camelCase(str)   // 'helloWorldEthiopia'
snakeCase(str)   // 'hello_world_ethiopia'
kebabCase(str)   // 'hello-world-ethiopia'
startCase(str)   // 'Hello World Ethiopia'
capitalize(str)  // 'Hello world ethiopia'
upperFirst(str)  // 'Hello world ethiopia'

// โœ‚๏ธ truncate โ€” แˆจแŒ…แˆ แ…แˆแแŠ• แˆˆแˆ›แˆณแŒ แˆญ (clip)
truncate('Hello World Ethiopia!', {
  length: 12,
  omission: '...'  // 'Hello Wor...'
})

// ๐Ÿ“ pad โ€” string alignment (แŠญแแ‰ฐแ‰ถแ‰ฝแŠ• แˆˆแˆ˜แˆ™แˆ‹แ‰ต)
pad('abc', 8)      // '  abc   '
padStart('5', 4, '0') // '0005' (แˆˆ IDs แ‹ญแŒ แ‰…แˆ›แˆ!)
padEnd('ET', 5, '-')  // 'ET---'

// ๐Ÿ›ก๏ธ Security (แˆˆแ‹ฐแˆ…แŠ•แАแ‰ต)
escape('<script>')   // '&lt;script&gt;'
words(str)           // ['hello','world','ethiopia']
function-methods.ts
import { debounce, throttle, memoize,
         once, partial, curry } from 'lodash';

// โฑ๏ธ debounce โ€” แ‰ฐแŒ แ‰ƒแˆšแ‹ แ…แŽ แŠฅแˆตแŠชแ‹ซแ‰†แˆ แ‹ญแŒ แ‰ฅแ‰ƒแˆ (แˆˆ Search input!)
const search = debounce((query) => {
  fetchResults(query); // 300ms แŠซแˆˆแˆ แ‰ แŠ‹แˆ‹ แ‰ฅแ‰ป แ‹ญแˆฐแˆซแˆ
}, 300);
input.addEventListener('input', (e) =>
  search(e.target.value));

// ๐Ÿšฆ throttle โ€” แ‰ แ‰ฐแ‹ˆแˆฐแА แŒŠแ‹œ แ‹แˆตแŒฅ แŠ แŠ•แ‹ด แ‰ฅแ‰ป แŠฅแŠ•แ‹ฒแˆฐแˆซ (แˆˆ Scroll!)
const onScroll = throttle(() => {
  updateProgress(); // แ‰ แ‹จ 200ms แŠ แŠ•แ‹ด แ‰ฅแ‰ป แ‹ญแˆฐแˆซแˆ
}, 200);

// ๐Ÿ’พ memoize โ€” แ‹จแˆฐแˆซแ‹แŠ• แˆตแˆŒแ‰ต แ‹แŒคแ‰ต แ‹ซแˆตแ‰€แˆแŒฃแˆ (Result Cache!)
const factorial = memoize((n) => {
  return n <= 1 ? 1 : n * factorial(n - 1);
});
factorial(100); // โœ… แˆแˆˆแ‰ฐแŠ› แŒŠแ‹œ แˆฒแŒ แˆซ แ‰€แŒฅแ‰ณ แŠจ cache แ‹ญแˆฐแŒ แŠ“แˆ!

// 1๏ธโƒฃ once โ€” แŠ แŠ•แ‹ด แ‰ฅแ‰ป แŠฅแŠ•แ‹ฒแˆฐแˆซ (แˆˆ Init functions!)
const initApp = once(() => {
  console.log('App initialized!');
});
initApp(); // แ‹ญแˆฐแˆซแˆ (prints)
initApp(); // โœ… แ‹แˆ แ‹ญแˆ‹แˆ โ€” แ‰ แ‹ตแŒ‹แˆš แŠ แ‹ญแˆฐแˆซแˆ!
collection-methods.ts
import { groupBy, sortBy, orderBy, keyBy,
         countBy, sumBy, maxBy, minBy,
         filter, map, reduce, find } from 'lodash';

const products = [
  { name:'Injera',  cat:'food',   price:50,  rating:4.8 },
  { name:'Tibs',    cat:'food',   price:200, rating:4.5 },
  { name:'Coffee',  cat:'drink',  price:30,  rating:4.9 },
  { name:'Tej',     cat:'drink',  price:80,  rating:4.2 },
];

// groupBy โ€” แ‰  Category แˆ˜แˆ˜แ‹ฐแ‰ฅ (แ‰กแ‹ตแŠ• แˆ›แ‹ตแˆจแŒ)
groupBy(products, 'cat')
// {food:[injera,tibs], drink:[coffee,tej]}

// sortBy / orderBy โ€” แˆ˜แ‹ฐแˆญแ‹ฐแˆญ
sortBy(products, 'price')
orderBy(products, ['rating'], ['desc'])

// keyBy โ€” ID lookup map (แ‰ แแŒฅแАแ‰ต แ‹ณแ‰ณ แˆˆแˆ›แŒแŠ˜แ‰ต)
keyBy(products, 'name')
// {Injera:{...}, Tibs:{...}}

// Aggregation (แ‹จแˆตแˆŒแ‰ต แˆตแˆซแ‹Žแ‰ฝ)
countBy(products, 'cat')    // {food:2,drink:2}
sumBy(products, 'price')   // 360
maxBy(products, 'rating') // Coffee object
minBy(products, 'price')  // Coffee {price:30}
04๐ŸŽฎ Live Playground โ€” แ‰€แŒฅแ‰ณ แˆžแŠญแˆฉแ‰ต!
๐Ÿ”ง Lodash Playground โ€” แ‹ณแ‰ณ แƒแ‰ โ†’ Function แˆแˆจแŒก โ†’ โ–ถ Run
โœ… READY
๐Ÿ“ฆ Input Array (JSON)
โš™๏ธ Function
๐Ÿ“‹ Input
โ€”
โœ… Output โ€” แ‹แŒคแ‰ต
โ–ท Run แŒ แ‰… แ‹ซแ‹ตแˆญแŒ‰ โ†’ แ‹แŒคแ‰ฑ แ‹ญแ‰ณแ‹ซแˆ!
05โ›“๏ธ Chaining โ€” _.chain() แ‰ แˆ˜แŒ แ‰€แˆ แˆ›แ‹ซแ‹ซแ‹!
_.chain() โ€” OperationsแŠ• แ‰ แ‰…แ‹ฐแˆ แ‰ฐแŠจแ‰ฐแˆ แˆ›แˆฐแˆˆแ!
CHAIN

_.chain() โ€” แ‰ฅแ‹™ operationsแŠ• แ‰€แŒฅ แŠ แ‹ตแˆญแŒŽ แ‹ซแ‹ซแ‹ญแ‹›แˆ! .value() แˆฒแ‹ฐแˆญแˆต แˆ‚แ‹ฐแ‰ฑ แ‹ญแŒ แŠ“แ‰€แ‰ƒแˆ! แˆˆแŠฅแ‹แАแ‰ฐแŠ› แ‹ณแ‰ณ แ•แˆฎแˆฐแˆฒแŠ•แŒ (Real-world data processing) แˆแˆญแŒก pattern แАแ‹!

โ‘ 
const orders = [ { user:'Samuel', amount:500, status:'paid', region:'Addis' }, { user:'Meron', amount:200, status:'pending', region:'Hawassa' }, { user:'Kebede', amount:800, status:'paid', region:'Addis' }, { user:'Tigist', amount:150, status:'paid', region:'Bahir Dar' }, { user:'Abel', amount:300, status:'pending', region:'Addis' }, ];
๐Ÿ“‹ แŒฅแˆฌ แ‹ณแ‰ณ (Raw data) โ€” 5 orders
โ‘ก
_.chain(orders) .filter({ status: 'paid' }) // โœ… แ‹จแ‰ฐแŠจแˆแˆ‰แ‰ตแŠ• (paid) แ‰ฅแ‰ป
โ†’ [Samuel-500, Kebede-800, Tigist-150]
โ‘ข
.groupBy('region') // ๐Ÿ“ แ‰  Region แˆ˜แˆ˜แ‹ฐแ‰ฅ
โ†’ {Addis:[Samuel,Kebede], 'Bahir Dar':[Tigist]}
โ‘ฃ
.mapValues(orders => _.sumBy(orders, 'amount')) // ๐Ÿ’ฐ แŠ แŒ แ‰ƒแˆ‹แ‹ญ แ‹ตแˆแˆญ แ‰ แ‹จ Region
โ†’ {Addis: 1300, 'Bahir Dar': 150}
โœ…
.value(); // ๐ŸŽฏ แ‹แŒคแ‰ฑแŠ• แ‹ซแ‹ˆแŒฃแˆ
โ†’ { Addis: 1300, 'Bahir Dar': 150 } โ€” แ‰ฐแŒ แŠ“แ‰€แ‰€! ๐ŸŽ‰
06โš–๏ธ Lodash vs Native JS โ€” แˆ˜แ‰ผ แ‹จแ‰ตแŠ›แ‹แŠ• แŠฅแŠ•แŒ แ‰€แˆ?

แ‹˜แˆ˜แŠ“แ‹Šแ‹ JS (ES2020+) แ‰ฅแ‹™ แ‹จ Lodash features แ‰ แˆซแˆฑ แŠ แŠซแ‰ทแˆ! แАแŒˆแˆญ แŒแŠ• แŠ แˆแŠ•แˆ Lodash แ‹จแˆšแ‹ซแˆธแŠ•แแ‰ฃแ‰ธแ‹ แ‰ฆแ‰ณแ‹Žแ‰ฝ แŠ แˆ‰!

แ‰ฐแŒแ‰ฃแˆญ (Task)โŒ Native JS (แˆจแ‹ฃแ‹ฅแˆ)โœ… Lodash (แ‰€แˆ‹แˆ‰)
Deep CloneJSON.parse(JSON.stringify(obj)) โ€” Dates/undefined แ‹ญแ‰ แˆ‹แˆปแˆ‰!_.cloneDeep(obj) โ€” แˆˆแˆแˆ‰แˆ type แ‹ญแˆฐแˆซแˆ โœ…
Deep Mergeแ‰ฅแ‹™ recursive code แˆ˜แƒแ แ‹ซแˆตแˆแˆแŒ‹แˆ_.merge(obj1, obj2) โ€” แ‰  1 แˆ˜แˆตแˆ˜แˆญ
Group Byarr.reduce((acc,x)=>{acc[x.k]=acc[x.k]||[];acc[x.k].push(x);return acc},{})_.groupBy(arr, 'key') โ€” แ‰  1 แˆ˜แˆตแˆ˜แˆญ!
Uniq ObjectsFilter + JSON.stringify comparison_.uniqBy(arr, 'id') โ€” แ‰  property แ‰ฅแ‰ป
DebounceManual setTimeout/clearTimeout_.debounce(fn, 300) โ€” แˆˆ production แ‹แŒแŒ
Safe Getobj?.a?.b?.c?.d (optional chain)_.get(obj, 'a.b.c.d', default)
Sum Byarr.reduce((s,x)=>s+x.price, 0)_.sumBy(arr, 'price') โ€” แŠ•แแˆ… แŠฅแŠ“ แŠ แŒญแˆญ
ChunkCustom loop + slice logic_.chunk(arr, size) โ€” แˆˆ pagination!
07โšก Production Ready โ€” แ‹จแŠฅแ‹แАแ‰ฐแŠ› แ‹“แˆˆแˆ แˆแˆณแˆŒ
productionUtils.ts โ€” EthioShop Dashboard
import { debounce, groupBy, orderBy, sumBy,
         maxBy, cloneDeep, pick, uniqBy,
         chunk, memoize } from 'lodash';

// 1๏ธโƒฃ Search โ€” แŠจ debounce แŒ‹แˆญ (แŠ แˆ‹แˆตแˆแˆ‹แŒŠ API calls แ‹ญแ‰€แŠ•แˆณแˆ!)
const handleSearch = debounce(async (query: string) => {
  const results = await searchProducts(query);
  setProducts(results);
}, 350);

// 2๏ธโƒฃ Dashboard analytics โ€” แˆแˆ‰แˆ operations แ‰ฐแ‹ซแ‹ญแ‹˜แ‹ (chain)!
const getDashboardData = memoize((orders: Order[]) => {
  const paid = orders.filter(o => o.status === 'paid');
  return {
    byRegion:    groupBy(paid, 'region'),
    totalRevenue:sumBy(paid, 'amount'),
    topOrder:    maxBy(paid, 'amount'),
    topProducts: orderBy(paid, ['amount'],['desc']).slice(0,5),
  };
}); // memoize โ†’ แ‰ฐแˆ˜แˆณแˆณแ‹ญ input แˆฒแˆ˜แŒฃ? cache แ‹ซแ‹ฐแˆญแŒ‹แˆ โ†ฉ

// 3๏ธโƒฃ Safe user update โ€” แŠจ cloneDeep แŒ‹แˆญ!
const updateUser = (user: User, updates: Partial<User>) => {
  const safe = cloneDeep(user);       // original แ‹ณแ‰ณแ‹ แŠ แ‹ญแАแŠซแˆ!
  return { ...safe, ...pick(updates,
    ['name','phone','address'])   // แ‹จแ‰ฐแˆแ‰€แ‹ฑ fields แ‰ฅแ‰ป
  };
};

// 4๏ธโƒฃ Pagination โ€” แŠจ chunk แŒ‹แˆญ!
const paginate = (items: any[], pageSize = 10) =>
  chunk(items, pageSize);
// paginate(products, 10) โ†’ [[p1..p10],[p11..p20]...]

// 5๏ธโƒฃ แ‹จแ‰ฐแ‹ฐแŒ‹แŒˆแˆ™ แ‰ฐแŒ แ‰ƒแˆšแ‹Žแ‰ฝแŠ• (users) แ‰  email แˆ˜แˆˆแ‹จแ‰ต
const deduplicateUsers = (users: User[]) =>
  uniqBy(users, 'email');
๐Ÿ“ŒแŒ แ‰…แˆˆแˆ แ‹ซแˆˆ แˆ›แˆตแ‰ณแ‹ˆแˆป
1
โฑ๏ธ debounce + throttle โ€” แˆˆ Performance!

Search input โ†’ debounce(fn, 300) โ€” แŠแ‹ฐแˆ แ‰ แ‰ฐแƒแˆ แ‰แŒฅแˆญ API แŠ แ‹ญแŒ แˆซแˆ! Scroll โ†’ throttle(fn, 200) โ€” แ‰ แˆฐแŠจแŠ•แ‹ต max 5 แŒŠแ‹œ! แˆˆ UI performance แˆแˆŒแˆ แŠ แˆตแˆแˆ‹แŒŠ แŠ“แ‰ธแ‹!

debounce = waits for pause ยท throttle = limits frequency.
2
๐Ÿช† cloneDeep โ€” Deep Clone แˆˆแˆ›แ‹ตแˆจแŒ แ‰ฅแ‰ธแŠ› แˆ˜แแ‰ตแˆ”!

JSON.parse(JSON.stringify()) Dates แฃ undefined แฃ Functions แ‹ซแ‰ แˆ‹แˆปแˆ! _.cloneDeep() แŒแŠ• แˆแˆ‰แŠ•แˆ type แ‰ แ‰ตแŠญแŠญแˆ clone แ‹ซแ‹ฐแˆญแŒ‹แˆ!

Always use cloneDeep when modifying state copies in React!
3
๐Ÿ“Š groupBy + sumBy โ€” แˆˆ Data Analytics!

แˆˆ Dashboard แฃ Reports แฃ Charts แˆแˆ‰ แ‹ซแŒˆแˆˆแŒแˆ‹แˆ! groupBy(orders, 'region') + sumBy(group, 'amount') โ€” แ‰€แˆ‹แˆ‰ แ‹จ analytics pipeline!

_.chain() with groupBy โ†’ mapValues โ†’ sumBy = most powerful pattern.
4
โ›“๏ธ _.chain() โ€” Pipeline Pattern!

แ‰ฅแ‹™ operations แ‰ แ‰…แ‹ฐแˆ แ‰ฐแŠจแ‰ฐแˆ chain แŠ แ‹ตแˆญแŒ‰! _.chain(data).filter().groupBy().mapValues().value() โ€” แˆˆแˆ›แŠ•แ‰ แ‰ฅ แ‰ แŒฃแˆ แ‰€แˆ‹แˆ แАแ‹!

Always end with .value() โ€” lazy evaluation!
5
๐ŸŒฒ Tree-shaking โ€” Bundle Size!

import _ from 'lodash' = แˆแˆ‰แŠ•แˆ แ‹ซแˆ˜แŒฃแˆ (70KB)! import { debounce } from 'lodash' = แ‹ซแŠ•แŠ• function แ‰ฅแ‰ป แ‹ซแˆ˜แŒฃแˆ! แˆˆ Production แˆแˆŒแˆ named import แ‰ฐแŒ แ‰€แˆ™!

Better: import debounce from 'lodash/debounce' โ€” smallest bundle!
๐ŸŽฏแˆˆแ‹•แ‹แ‰€แ‰ต แˆ˜แˆแ‰ฐแŠ› Quiz!
โ“ Search input แˆ‹แ‹ญ แŠแ‹ฐแˆ แˆฒแŒปแ แŠฅแ‹ซแŠ•แ‹ณแŠ•แ‹ฑ แŠแ‹ฐแˆ API call แŠฅแŠ•แ‹ณแ‹ซแ‹ฐแˆญแŒแฃ แ‰ฐแŒ แ‰ƒแˆšแ‹ แ…แŽ แˆฒแ‹ซแ‰†แˆ แ‰ฅแ‰ป call แˆˆแˆ›แ‹ตแˆจแŒ แ‹จแˆšแŒ แ‰…แˆ˜แ‹ Lodash function แ‹จแ‰ฑ แАแ‹?
Which Lodash function delays execution until after the user stops typing?
A
_.throttle() โ€” max rate limit แ‹ซแ‹ฐแˆญแŒ‹แˆ
B
_.debounce() โ€” แ…แˆแ‹ แˆฒแ‹ซแ‰†แˆ™ แ‰ฅแ‰ป แ‹ญแˆฐแˆซแˆ! โœ…
C
_.memoize() โ€” result caching แ‹ซแ‹ฐแˆญแŒ‹แˆ
D
_.once() โ€” แŠ แŠ•แ‹ด แ‰ฅแ‰ป แ‹ญแˆฐแˆซแˆ