阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈

標題說明了一切🐐。我想談談我一直以來最喜歡的 JavaScript 陣列方法: Array.reduce() 。我知道有很多競爭者,但請聽我說完。 reduce() 不只是一個方法;這是一種生活方式✨。

我不想撒謊,當我第一次開始並發現reduce 時,它有點令人生畏。我花了一段時間才在程式碼中自信地使用它。但當我這麼做的時候,遊戲規則就改變了。突然間,我可以輕鬆地對陣列執行複雜的操作,將它們轉換為我需要的任何內容。我的程式碼變得更快更乾淨。

但不要只相信我的話。讓我向您展示使用reduce() 可以實現的一些功能。是時候深入研究 Array.reduce() 並發現為什麼它絕對是山羊了! 🐐

Array.reduce() 的 9 個用例 🐐

用例 1:對數字求和

reduce() 最直接的用例之一是對一堆數字求和。假設您有一個整數陣列,並且您想要找到總和。

const numbers: number[] = [1, 2, 3, 4, 5];
const sum: number = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // Output: 15

繁榮!只需一行程式碼,您就可以計算出陣列中所有元素的總和。累加器的初始值設為 0,並且在每次迭代中,我們將當前元素新增至累加器。

** 獎勵:如果您選擇忽略起始值,reduce 將只使用陣列中的第一項。不過我傾向於總是包含一個初始值,這樣比較容易閱讀。

用例 2:展平陣列

您是否曾經發現自己有一個陣列陣列並想:“我希望我可以將其扁平化為一個陣列”?

const nestedArray: number[][] = [[1, 2], [3, 4], [5, 6]];
const flattenedArray: number[] = nestedArray.reduce((acc, curr) => acc.concat(curr), []);
console.log(flattenedArray); // Output: [1, 2, 3, 4, 5, 6]

在此範例中,我們從一個空陣列開始作為初始累加器值。然後,在每次迭代中,我們使用 concat() 方法將目前子陣列連接到累加器。最後,我們就有了一個完美的扁平化陣列。

我知道您也可以使用Array.flat()來做到這一點。然而,了解如何使用reduce 很重要,以防您想對每個專案執行額外的操作。

用例 3:對物件進行分組

假設您有一個物件陣列,並且您希望根據特定屬性對它們進行分組。 reduce() 是完成這項工作的完美工具。

interface Person {
  name: string;
  age: number;
}

const people: Person[] = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge: { [key: number]: Person[] } = people.reduce((acc, curr) => {
  if (!acc[curr.age]) {
    acc[curr.age] = [];
  }
  acc[curr.age].push(curr);
  return acc;
}, {});

console.log(groupedByAge);
/*
Output:
{
  '25': [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
  '30': [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }]
}
*/

在本例中,我們使用一個物件作為初始累加器值。我們檢查累加器是否已經具有當前年齡的屬性。如果沒有,我們為該年齡建立一個空陣列。然後,我們將目前物件推入對應的年齡陣列中。最後,我們得到一個物件,其中鍵是年齡,值是該年齡的人的陣列。

現在您也可以使用更新的groupBy方法,但是,理解這個久經考驗的真正經典方法很重要。

用例 4:建立查找圖

我個人最喜歡的是使用reduce()從陣列建立查找映射。在效能和程式碼可讀性方面,它改變了遊戲規則。停止使用那些緩慢的 find() 或 filter() 呼叫。

interface Product {
  id: number;
  name: string;
  price: number;
}

const products: Product[] = [
  { id: 1, name: 'Laptop', price: 999 },
  { id: 2, name: 'Phone', price: 699 },
  { id: 3, name: 'Tablet', price: 499 },
];

const productMap: { [key: number]: Product } = products.reduce((acc, curr) => {
  acc[curr.id] = curr;
  return acc;
}, {});

console.log(productMap);
/*
Output:
{
  '1': { id: 1, name: 'Laptop', price: 999 },
  '2': { id: 2, name: 'Phone', price: 699 },
  '3': { id: 3, name: 'Tablet', price: 499 }
}
*/

// Accessing a product by ID
const laptop: Product = productMap[1];
console.log(laptop); // Output: { id: 1, name: 'Laptop', price: 999 }

透過使用reduce()建立查找映射,您可以以恆定的時間複雜度透過元素的唯一辨識碼來存取元素。不再需要循環遍歷陣列來尋找特定專案。

用例 5:計算出現次數

曾經需要計算陣列中元素的出現次數嗎? reduce() 已經幫你解決了。

const fruits: string[] = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];

const fruitCounts: { [key: string]: number } = fruits.reduce((acc, curr) => {
  acc[curr] = (acc[curr] || 0) + 1;
  return acc;
}, {});

console.log(fruitCounts);
/*
Output:
{
  'apple': 3,
  'banana': 2,
  'orange': 1
}
*/

在這個例子中,我們初始化一個空物件作為累加器。對於陣列中的每個水果,我們檢查它是否已作為累加器物件中的屬性存在。如果是,我們將其計數加 1;否則,我們將其初始化為 1。

用例 6:組合函數

函數式程式設計愛好者一定會喜歡這個。 reduce() 是一個強大的函陣列合工具。您可以使用它來建立逐步轉換資料的函數管道。

const add5 = (x: number): number => x + 5;
const multiply3 = (x: number): number => x * 3;
const subtract2 = (x: number): number => x - 2;

const composedFunctions: ((x: number) => number)[] = [add5, multiply3, subtract2];

const result: number = composedFunctions.reduce((acc, curr) => curr(acc), 10);
console.log(result); // Output: 43

在這個範例中,我們有一個函數陣列,我們希望將其按順序應用到初始值 10。最終結果是按組合順序應用所有函數的結果。

用例 7:實作簡單的類似 Redux 的狀態管理

如果您使用過 Redux,您就會知道它在管理應用程式中的狀態方面有多強大。你猜怎麼了?你可以使用reduce()來實作一個簡單的類似Redux的狀態管理系統。

interface State {
  count: number;
  todos: string[];
}

interface Action {
  type: string;
  payload?: any;
}

const initialState: State = {
  count: 0,
  todos: [],
};

const actions: Action[] = [
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Learn Array.reduce()' },
  { type: 'INCREMENT_COUNT' },
  { type: 'ADD_TODO', payload: 'Master TypeScript' },
];

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'INCREMENT_COUNT':
      return { ...state, count: state.count + 1 };
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, action.payload] };
    default:
      return state;
  }
};

const finalState: State = actions.reduce(reducer, initialState);
console.log(finalState);
/*
Output:
{
  count: 2,
  todos: ['Learn Array.reduce()', 'Master TypeScript']
}
*/

在此範例中,我們有一個初始狀態物件和一組操作。我們定義一個reducer函數,它接受當前狀態和一個動作,並根據動作類型返回一個新狀態。透過使用reduce(),我們可以將每個動作依序應用於狀態,從而得到最終狀態。這就像擁有一個迷你 Redux。

用例 8:產生唯一值

有時,您可能有一個包含重複值的陣列,並且您需要僅提取唯一的值。 reduce() 可以幫助您輕鬆實現這一點。

const numbers: number[] = [1, 2, 3, 2, 4, 3, 5, 1, 6];

const uniqueNumbers: number[] = numbers.reduce((acc, curr) => {
  if (!acc.includes(curr)) {
    acc.push(curr);
  }
  return acc;
}, []);

console.log(uniqueNumbers); // Output: [1, 2, 3, 4, 5, 6]

在這裡,我們初始化一個空陣列作為累加器。對於原始陣列中的每個數字,我們使用includes()方法檢查它是否已存在於累加器中。如果沒有,我們將其推入累加器陣列。最終結果是一個僅包含原始陣列中唯一值的陣列。

用例 9:計算平均值

想要計算一組數字的平均值? reduce() 為您提供支援!

const grades: number[] = [85, 90, 92, 88, 95];

const average: number = grades.reduce((acc, curr, index, array) => {
  acc += curr;
  if (index === array.length - 1) {
    return acc / array.length;
  }
  return acc;
}, 0);

console.log(average); // Output: 90

在此範例中,我們將累加器初始化為 0。當我們到達最後一個元素時(使用索引和 array.length 檢查),我們將累加器除以成績總數來計算平均值。

性能考量🏎️

雖然 Array.reduce() 非常強大且用途廣泛,但了解潛在的效能缺陷非常重要,尤其是在處理大型陣列或複雜操作時。一個常見的陷阱是在每次reduce()迭代中建立新的物件或陣列,這可能會導致過多的記憶體分配並影響效能。

例如,考慮以下程式碼:

const numbers: number[] = [1, 2, 3, 4, 5];

const doubledNumbers: number[] = numbers.reduce((acc, curr) => {
  return [...acc, curr * 2];
}, []);

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

在本例中,我們使用展開運算子 (...) 在每次迭代中建立一個新陣列,這可能會效率低下。相反,我們可以透過直接改變累加器陣列來優化程式碼:

const numbers: number[] = [1, 2, 3, 4, 5];

const doubledNumbers: number[] = numbers.reduce((acc, curr) => {
  acc.push(curr * 2);
  return acc;
}, []);

console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

透過使用 push() 來改變累加器陣列,我們可以避免在每次迭代中建立新陣列,從而獲得更好的效能。

類似地,在處理物件時,直接改變累加器物件比使用擴充運算子建立新物件更有效:

const people: Person[] = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge: { [key: number]: Person[] } = people.reduce((acc, curr) => {
  if (!acc[curr.age]) {
    acc[curr.age] = [];
  }
  acc[curr.age].push(curr);
  return acc;
}, {});

透過直接改變累加器物件,我們優化了reduce()操作的效能。

然而,值得注意的是,在某些情況下,在每次迭代中建立新的物件或陣列可能是必要的或更具可讀性。根據您的具體用例和您正在使用的資料大小,在效能和程式碼清晰度之間取得平衡非常重要。

結論

你有它。九個令人難以置信的用例展示了 Array.reduce() 的強大功能和多功能性。從將數字求和到展平陣列、對物件進行分組到建立查找映射、對出現次數進行計數到組合函數,甚至實現狀態管理和計算平均值, Array.reduce()被證明是js 工具包中的強大工具。

你怎麼認為?您最喜歡的陣列方法是什麼?

感謝您的閱讀,願 reduce() 的力量與您同在。 ✨🐐✨


原文出處:https://dev.to/mattlewandowski93/arrayreduce-is-goated-1f1j


共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。

阿川私房教材:學程式,拿 offer!

63 個專案實戰,直接上手!
無需補習,按步驟打造你的面試作品。

立即解鎖你的轉職秘笈