[JS] μλ°μ€ν¬λ¦½νΈ reduce() λ©μλ νμ© μμ λͺ¨μ
Array.reduce()
λ©μλλ λ°°μ΄μ μλ κ° μμμ μ½λ°± ν¨μλ₯Ό μ€ννμ¬ λμ λ κ³μ° κ²°κ³Όλ₯Ό νλμ κ°μΌλ‘ λ°ννλ€. μλ°μ€ν¬λ¦½νΈμμ μ 곡νλ filter()
, map()
λ±μ λ°°μ΄ λ©μλλ λͺ¨λ reduce()
λ‘ κ΅¬νν μ μκ³ , μ΄ μΈμλ λ€μν νμ© λ°©λ²μ΄ μκΈ° λλ¬Έμ κ°μΈμ μΌλ‘ κ°μ₯ μμ£Ό μ¬μ©νλ λ°°μ΄ λ©μλλ€. reduce()
λ©μλμ ꡬ문μ μλμ κ°λ€.
reduce(callbackFn, initialValue?)
callbackFn
μ½λ°± νλΌλ―Έν°accumulator
: μ΄μ callbackFn
λ°νκ°- μ΄κΈ°κ° μ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
initialValue
- μ΄κΈ°κ° λ―Έμ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
array[0]
- μ΄κΈ°κ° μ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
currentValue
: νμ¬ μννκ³ μλ κ°- μ΄κΈ°κ° μ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
array[0]
- μ΄κΈ°κ° λ―Έμ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
array[1]
- μ΄κΈ°κ° μ§μ μ μ½λ°±μ μ²μ νΈμΆνμ λ κ° :
currentIndex
: νμ¬ μννκ³ μλ κ°μ μΈλ±μ€array
: reduceλ₯Ό νΈμΆν λ°°μ΄
initialValue
(μ΄κΈ°κ°) :callbackFn
μ½λ°±μ μ²μ νΈμΆν λ accumulatorμ μ¬μ©ν μ΄κΈ°κ°. λ―Έμ§μ μ λ°°μ΄(array) 첫 λ²μ§Έ μΈμλ₯Ό μ΄κΈ°κ°μΌλ‘ μ¬μ©. μ΄κΈ°κ° μ§μ μ λ°°μ΄ μ²« λ²μ§Έ μμλ μνμ ν¬ν¨ μ ν¨.
μ«μ λνκΈ° | Summing Numbers
reduce()
λ©μλμ κ°μ₯ κΈ°λ³Έμ μΈ μ¬μ© μμ. μ«μλ‘ μ΄λ€μ§ λ°°μ΄μ΄ μ£Όμ΄μ‘μ λ λͺ¨λ μλ₯Ό λν κ°μ λ°ννλ€.
const odds = [1, 3, 5, 7, 9, 11];
const sumOfOdds = odds.reduce((acc, cur) => acc + cur);
console.log(sumOfOdds); // 36
λ°°μ΄ νΌμΉκΈ° | Flattening Arrays
Array.flat()μ μ€μ²©λ λ°°μ΄μ νΌμ³μ λ¨μΌ λ°°μ΄λ‘ λ§λλ λ©μλλ€. reduce()
λ₯Ό μ΄μ©νλ©΄ flat()
λ©μλμ λμΌνκ² κ΅¬νν μ μλ€.
const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flattenedArray = nestedArray.reduce((acc, cur) => acc.concat(cur), []);
console.log(flattenedArray); // [1, 2, 3, 4, 5, 6]
μ μμλ κΈ°λ³Έμ μΌλ‘ 1 depthλ§ νΌμΉ μ μλ€. 2 depth μ΄μ μ€μ²©λ λ°°μ΄μ΄λΌλ©΄ μ¬κ·λ₯Ό μ΄μ©νλ©΄ λλ€.
const nestedArray = [1, 2, [3, 4, [5, 6]]];
const deepFlat = (arr) => {
return arr.reduce((acc, cur) => {
return acc.concat(Array.isArray(cur) ? deepFlat(cur) : cur);
}, []);
};
const flattenedArray = deepFlat(nestedArray);
console.log(flattenedArray); // [1, 2, 3, 4, 5, 6]
κ°μ²΄ κ·Έλ£Ήν | Grouping Objects
κ°μ²΄λ₯Ό μμλ‘ κ°λ λ°°μ΄μμ, νΉμ νλ‘νΌν°λ₯Ό κΈ°μ€μΌλ‘ κ·Έλ£Ήν(Grouping) ν΄μΌ νλ μν©μ μμ£Ό μ νλ€. μλ₯Ό λ€μ΄ μλμ²λΌ age
μ°λ Ήλ³λ‘ κ·Έλ£Ήμ λλλ μν©μ μκ°ν΄ λ³Ό μ μλ€.
// μλ³Έ λ°°μ΄
[
{ name: 'A', age: 20 },
{ name: 'B', age: 30 },
{ name: 'C', age: 20 },
{ name: 'D', age: 30 },
];
// μ°λ Ήλ³λ‘ κ·Έλ£Ήν
{
20: [
{ name: 'A', age: 20 },
{ name: 'C', age: 20 },
],
30: [
{ name: 'B', age: 30 },
{ name: 'D', age: 30 },
],
};
μ΄κΈ°κ°μ κ°μ²΄λ‘ μ§μ ν reduce()
λ₯Ό νμ©νλ©΄ κ°νΈνκ² κ·Έλ£Ήνν μ μλ€. κ° age
ν€λ§λ€ λ°°μ΄μ μμ±νκ³ , νμ¬ μννλ κ°μ²΄μ age
μμ±μ ν΄λΉνλ λ°°μ΄μ push νλ λ°©μμΌλ‘ ꡬννλ©΄ λλ€.
const users = [
{ name: 'A', age: 20 },
{ name: 'B', age: 30 },
{ name: 'C', age: 20 },
{ name: 'D', age: 30 },
];
const groupByAge = users.reduce((acc, cur) => {
const { age } = cur;
if (!acc[age]) acc[age] = [];
acc[age].push(cur);
return acc;
}, {});
console.log(groupByAge);
/*
{
20: [{ name: 'A', age: 20 }, { name: 'C', age: 20 }],
30: [{ name: 'B', age: 30 }, { name: 'D', age: 30 }]
}
*/
μ°Έκ³ λ‘ κ°μ²΄ κ·Έλ£Ήνμ ES15(ECMAScript 2024)λΆν° μ 곡νλ groupBy() λ©μλλ₯Ό μ΄μ©ν μλ μλ€. μ΄ λ©μλλ Chrome 117 λ²μ , Node.js 21 λ²μ μ΄νλΆν° μ¬μ©ν μ μλ€(Baseline 2024).
const groupByAge = Object.groupBy(users, ({ age }) => age);
console.log(groupbyAge); // {20: Array(2), 30: Array(2)}
μ‘°νμ© λ§΅ | Lookup Maps
λ°°μ΄ κ° μμκ° κ°μ²΄μ΄κ³ , κ° κ°μ²΄λ κ³ μ ν id
νλ‘νΌν°λ₯Ό κ°μ§ λ, id
λ‘ λΉ λ₯΄κ² μ‘°νν μ μλ Lookup λ§΅μ μμ± ν΄λλ©΄, νΉμ id
λ₯Ό κ°μ§ μμμ O(1) μκ° λ³΅μ‘λλ‘ λΉ λ₯΄κ² μ κ·Όν μ μλ€. λ§€λ² find()
κ°μ λ©μλλ‘ μνν νμκ° μκΈ° λλ¬Έμ μ±λ₯ λ©΄μμ λ μ 리νλ€.
const products = [
{ id: 1, name: 'iPhone' },
{ id: 2, name: 'iPad' },
{ id: 3, name: 'iMac' },
];
const productMap = products.reduce((acc, cur) => {
acc[cur.id] = cur;
return acc;
}, {});
console.log(productMap);
/*
{
1: { id: 1, name: 'iPhone' },
2: { id: 2, name: 'iPad' },
3: { id: 3, name: 'iMac' }
}
*/
console.log(productMap[3]); // { id: 3, name: 'iMac' }
νμ κ³μ° | Counting Occurrences
λ°°μ΄ λ΄ νΉμ μμκ° λͺ κ° μλμ§ κ³μ°ν λλ reduce()
λ©μλλ₯Ό νμ©ν μ μλ€. λΉ κ°μ²΄λ₯Ό μ΄κΈ°κ°μΌλ‘ μ§μ νκ³ , κ° μμλ₯Ό κ°μ²΄ ν€λ‘ μ¬μ©νμ¬ μνν λλ§λ€ ν€μ ν΄λΉνλ μΉ΄μ΄νΈλ₯Ό 1μ© μ¦κ°νλ λ°©μμΌλ‘ ꡬννλ€.
const products = ['iPhone', 'iMac', 'MacBook', 'iPhone', 'iMac', 'iPhone'];
const productCounts = products.reduce((acc, cur) => {
acc[cur] = (acc[cur] ?? 0) + 1;
return acc;
}, {});
console.log(productCounts); // { iPhone: 3, iMac: 2, MacBook: 1 }
ν¨μ ν©μ± | Composing Functions
ν¨μ ν©μ±μ μ¬λ¬ ν¨μλ₯Ό νλμ ν¨μλ‘ κ²°ν©νμ¬, κ° ν¨μμ μΆλ ₯μ λ€μ ν¨μμ μ
λ ₯μΌλ‘ μ¬μ©νλ λ°©μμΌλ‘ ν¨μν νλ‘κ·Έλλ°μμ μμ£Ό μ¬μ©νλ ν¨ν΄μ΄λ€. ν¨μ ν©μ±μ f(g(x))
μ κ°μ΄ λ΄λΆ ν¨μλΆν° μΈλΆ ν¨μ μμΌλ‘ μ€νλλλ° reduce()
λ©μλλ₯Ό μ΄μ©ν΄μ ꡬνν μ μλ€.
const compose = (...funcs) => {
return (x) => funcs.reduce((acc, fn) => fn(acc), x);
};
const increment = (x) => x + 1;
const square = (x) => x * x;
const decrement = (x) => x - 1;
// (x) => func.reduce((acc, f) => f(acc), x)
const composedFunc = compose(increment, square, decrement);
console.log(composedFunc(5)); // 35
μ μμλ μλμ²λΌ ν¨μλ₯Ό μ€νν κ²κ³Ό λμΌνλ€.
// imcrement -> square -> decrement
decrement(square(increment(5)));
λ§μ½ ν¨μ μ€ν μμλ₯Ό λ°λλ‘ νκ³ μΆλ€λ©΄ μ€λ₯Έμͺ½ μμλΆν° μννλ reduceRight() λ©μλλ₯Ό μ¬μ©νλ©΄ λλ€.
μ€λ³΅ μ κ±° | Deduplication
λ°°μ΄μ μ€λ³΅ μ κ±°λ Set() κ°μ²΄λ₯Ό μ΄μ©νλ©΄ κ°λ¨νκ² μνν μ μλ€. νμ§λ§ νΉμ μμμ λν΄μ μ€λ³΅μ νμ©νλ λ± μΆκ°μ μΈ λ‘μ§μ μ μ©ν μ μλ€. μ΄λ° μμ
μμ reduce()
λ©μλλ₯Ό νμ©ν μ μλ€.
const numbers = [1, 1, 2, 3, 2, 2, 6, 8, 8, 11, 12];
const allowList = new Set([8]); // 8μ μ€λ³΅ νμ©
const uniqueNumbers = numbers.reduce((res, num) => {
if (!res.includes(num) || allowList.has(num)) res.push(num);
return res;
}, []);
console.log(uniqueNumbers); // [ 1, 2, 3, 6, 8, 8, 11, 12 ]
νκ· κ³μ° | Calculating Average
reduce()
λ©μλλ₯Ό μ΄μ©ν΄μ μ«μλ‘ μ΄λ€μ§ λ°°μ΄μ νκ· κ°μ κ³μ°ν μλ μλ€. κ° μμλ₯Ό μννλ©΄μ λͺ¨λ κ°μ λν ν, λ§μ§λ§ μμλ₯Ό μνν λ μ΄ν©μ μμ κ°μλ‘ λλ κ²°κ³Όλ₯Ό λ°ννλ©΄ λλ€.
const rates = [3, 2, 1, 5, 4, 3];
const average = rates.reduce((total, rate, idx, { length }) => {
const sum = total + rate;
if (idx === length - 1) return sum / length;
return sum;
}, 0);
console.log(average); // 3
νλ‘λ―Έμ€ μ²΄μ΄λ | Promise Chaining
μμ£Ό μ¬μ©νλ ν¨ν΄μ μλμ§λ§ reduce()
λ©μλλ₯Ό μ΄μ©ν΄μ νλ‘λ―Έμ€ μ²΄μΈμ ꡬμ±ν μλ μλ€. λλ μ΄ μκ°μ λνλ΄λ κ° μμλ₯Ό μνν λλ§λ€ prevPromise.then(...)
νλ‘λ―Έμ€λ₯Ό λ°ννκ³ , μ΄λ κ² λ°νλ νλ‘λ―Έμ€λ λ€μ μνμ prevPromise
κ° λΌμ λΉλκΈ° μμ
μ μμ°¨μ μΌλ‘ μ€ννλλ‘ λ§λ λ€.
const wait = (delay) => {
return new Promise((resolve, _reject) => {
console.log('[wait]', delay);
setTimeout(() => resolve(delay), delay);
});
};
const delayList = [1000, 2000, 3000, 4000];
delayList.reduce((prevPromise, curDelay) => {
console.log('[reduce]', prevPromise, curDelay);
// μνλ§λ€ prevPromise.then(...) νλ‘λ―Έμ€λ₯Ό λ°ννκ³ , λ€μ μνμ prevPromiseκ° λλ€
return prevPromise.then((res) => {
console.log('[then]', res);
return wait(curDelay);
});
}, Promise.resolve('start'));
/*
μ μ½λλ μλ νλ‘λ―Έμ€ μ²΄μΈκ³Ό λμΌνλ€:
Promise.resolve('start')
.then(() => wait(1000))
.then(() => wait(2000))
.then(() => wait(3000))
.then(() => wait(4000))
*/
μ½μ μΆλ ₯ κ²°κ³Όλ μλμ κ°λ€.
λͺ¨λ λ°°μ΄ λ©μλλ λκΈ°μ μΌλ‘ μλνκΈ° λλ¬Έμ μ½λ°± λ΄μμ λΉλκΈ° μμ μ μνν΄λ reduceλ νλ‘λ―Έμ€κ° μ²λ¦¬λ λκΉμ§ κΈ°λ€λ¦¬μ§ μκ³ λ€μ μ½λ°±μ μ€νμν¨λ€. μ¦, μ½λ°± λ΄μμ μνν λΉλκΈ° μμ μ reduce λ©μλμ μ μ΄λ₯Ό λ²μ΄λ λ³λλ‘ μ²λ¦¬λλ€.
λΉλκΈ°λ‘ νΈμΆλ ν¨μλ νμ€ν¬ νλ‘ λ€μ΄κ°λλ°, νμ μλ ν¨μλ€μ μ½μ€νμ΄ λΉμ΄μμ΄μΌλ§ μ€νλ μ μλ€. λ°λΌμ reduce λ©μλμ λͺ¨λ μνλ₯Ό μλ£ν νμ λΉλκΈ° μμ λ€μ΄ μ°¨λ‘λλ‘ μ€νλλ€.
π‘then
, catch
, finally
κ°μ νμ μ²λ¦¬ λ©μλλ νμ νλ‘λ―Έμ€λ₯Ό λ°ννλ€. νμ μ²λ¦¬ λ©μλ μΈμμ μ λ¬ν μ½λ°± ν¨μκ° νλ‘λ―Έμ€λ₯Ό λ°ννλ©΄ κ·Έλλ‘ λ°ννκ³ , νλ‘λ―Έμ€κ° μλ κ°μ λ°ννλ©΄ ν΄λΉ κ°μ μ묡μ μΌλ‘ resolve νΉμ reject ν΄μ νλ‘λ―Έμ€λ₯Ό μμ±ν ν λ°ννλ€. νλ‘λ―Έμ€κ° λκΈ°(pending) μνμ΄λ©΄ νμ μ²λ¦¬ λ©μλλ νλ‘λ―Έμ€κ° μ²λ¦¬λκΈΈ κΈ°λ€λ¦¬κ³ , μ΄λ―Έ μ²λ¦¬(settled) μνλΌλ©΄ νΈλ€λ¬λ μ¦κ° μ€νλλ€.
Immutable vs Mutable
reduce λ©μλλ ν¬κ² λΆλ³(Immutable)κ³Ό κ°λ³(Mutable) 2κ°μ§ μ¬μ© λ°©μμΌλ‘ μ¬μ©ν μ μλ€. λΆλ³μ μ΄λ¦ κ·Έλλ‘ ν λ² μμ±ν ν λ³κ²½ν μ μλ κ°μ, κ°λ³μ μμ±ν νμλ λ³κ²½ν μ μλ κ°μ μλ―Ένλ€. μλ°μ€ν¬λ¦½νΈμμ μμν λ°μ΄ν° νμ μ΄ λΆλ³μ±μ κ°μ§λ©°, μ°Έμ‘°νμ κΈ°λ³Έμ μΌλ‘ κ°λ³μ±μ κ°μ§λ€.
λΆλ³μ μΈ μ¬μ© λ°©μμ μμλ₯Ό μνν λλ§λ€ κΈ°μ‘΄ λμ κ°μ λ³κ²½νμ§ μκ³ νμ μλ‘μ΄ κ°μ²΄λ λ°°μ΄μ μμ±νλ λ°©μμ κ°λ¦¬ν¨λ€. κΈ°μ‘΄ μνλ₯Ό λ³κ²½νμ§ μκΈ° λλ¬Έμ μ¬μ΄λ μ΄ννΈλ₯Ό λ°©μ§ν μ μκ³ κ°λ μ±μ΄ μ’μ λμ , λ©λͺ¨λ¦¬ μ¬μ©λμ΄ μ¦κ°ν μ μλ λ¨μ μ΄ μλ€.
// Immutable
const doubledNumbers = [1, 2, 3].reduce((acc, cur) => {
return [...acc, cur * 2];
}, []);
κ°λ³μ μΈ μ¬μ© λ°©μμ κΈ°μ‘΄ λμ κ°μ μ§μ λ³κ²½νμ¬ κ²°κ³Όλ₯Ό λ§λλ λ°©μμ κ°λ¦¬ν¨λ€. κΈ°μ‘΄ κ°μ²΄λ λ°°μ΄μ μ§μ μμ νκΈ° λλ¬Έμ λ©λͺ¨λ¦¬ μ¬μ©λμ΄ μ μ λμ , μ½λ κ°λ μ±μ΄ λ¨μ΄μ§κ³ μ¬μ΄λ μ΄ννΈκ° λ°μν κ°λ₯μ±μ΄ μλ€.
// Mutable
const doubledNumbers = [1, 2, 3].reduce((acc, cur) => {
acc.push(cur * 2);
return acc;
}, []);
λΆλ³μ±κ³Ό κ°λ³μ± κ°κ° μ₯λ¨μ μ΄ μκΈ° λλ¬Έμ μ΄λ€ λ°©μμ΄ νμ μ³λ€κ³ λ¨μ ν μλ μλ€. λ°μ΄ν° μμ΄ λ§κ³ μ±λ₯μ΄ μ€μν κ²½μ°μ κ°λ³μ±μ μ¬μ©νλ κ² μ 리ν μ μλ€. λ°λ©΄, μ½λ κ°λ μ±μ΄λ μ μ§λ³΄μμ±μ΄ μ€μνλ€λ©΄ λΆλ³μ±μ κ³ λ €ν μ μλ€.
λ νΌλ°μ€
- Array.reduce() is Goated πβ¨
- Object.groupBy() - JavaScript | MDN
- Why Using Reduce() To Sequentially Resolve Promises Works | CSS-Tricks
κΈ μμ μ¬νμ λ Έμ νμ΄μ§μ κ°μ₯ λΉ λ₯΄κ² λ°μλ©λλ€. λ§ν¬λ₯Ό μ°Έκ³ ν΄ μ£ΌμΈμ