紀錄工作經驗、相關知識,解決技術相關問題。

Javascript, React, 新手 Jamp Holes, 網站相關

JS 陣列加總與平均值怎麼算?Array.reduce 初始值設定與物件求和範例

JS 陣列加總與平均值怎麼算?Array.reduce 初始值設定與物件求和範例 特色圖片

在前端開發實務中,處理 JS 陣列加總 是非常高頻的常見需求——在實作電商購物車總金額、訂單報表統計或評分平均時,陣列數字加總(Array Sum)與平均值計算是前端最常見的資料處理邏輯。繼先前介紹 JS 陣列取得最大值與最小值的 5 種方法 後,本文將深入拆解 Array.prototype.reduce() 在純數字陣列與「物件陣列(Array of Objects)」求和時的初始值陷阱與型別防呆。


一、 核心複製即用範本與陣列加總方法對照表

針對不同的陣列結構(純數字陣列 vs 購物車商品物件陣列),以下整理最常用的加總與平均計算方式:

計算需求場景建議語法結構空陣列 [] 回傳值
純數字陣列加總(Sum)arr.reduce((acc, cur) => acc + Number(cur), 0)0(安全不報錯)
物件陣列特定欄位加總items.reduce((acc, item) => acc + item.price * item.qty, 0)0(安全不報錯)
陣列平均值計算(Average)arr.length === 0 ? 0 : sum / arr.length0(防範 0 / 0 = NaN)
超大陣列迴圈加總for (const n of arr) sum += n0(無回呼函式開銷)

二、 分項語法拆解與 reduce 初始值陷阱

1. 純數字陣列加總:為什麼 reduce 一定要寫第二個參數 0?

使用 Array.prototype.reduce((accumulator, currentValue) => accumulator + currentValue, initialValue) 時,許多新手會省略最後面的初始值 0。

省略初始值在陣列有資料時看似正常,但一旦後端 API 回傳空陣列 [],省略初始值的 reduce 就會直接拋出 TypeError: Reduce of empty array with no initial value 導致整個頁面崩潰!

const prices = [120, 250, 80, 50];

// ✅ 務必傳入第二個參數 0 作為初始累加值 (initialValue)
const total = prices.reduce((acc, cur) => acc + cur, 0);
console.log(total); // 輸出: 500

// 當遇到空陣列時:
const emptyList = [];
console.log(emptyList.reduce((acc, cur) => acc + cur, 0)); // 輸出: 0(正常運作)

// ❌ 若未設定初始值 0,遇到空陣列會直接拋出 TypeError 崩潰!
// emptyList.reduce((acc, cur) => acc + cur);
// -> Uncaught TypeError: Reduce of empty array with no initial value

2. 物件陣列(購物車商品清單)金額加總

在真實專案中,API 回傳的資料幾乎都是物件陣列(例如 [{ name: '滑鼠', price: 500, qty: 2 }, ...])。如果這時忘記傳入初始值 0,reduce 會把第一個物件當成初始值 acc,並從第二個元素開始執行回呼,第一次就是 物件 + 數字,物件被轉成字串 "[object Object]" 後一路變成字串串接。以下方的 cartItems 為例,省略初始值會得到 "[object Object]1700300",而不是數字 4400!

const cartItems = [
  { id: 1, name: '機械鍵盤', price: 2400, qty: 1 },
  { id: 2, name: '無線滑鼠', price: 850, qty: 2 },
  { id: 3, name: '滑鼠墊', price: 300, qty: 1 },
];

// ✅ 初始值設為 0,每次累加單價乘以數量
const cartTotal = cartItems.reduce((acc, item) => {
  const price = Number(item.price) || 0;
  const qty = Number(item.qty) || 0;
  return acc + price * qty;
}, 0);

console.log(cartTotal); // 輸出: 4400 (2400*1 + 850*2 + 300*1)

算出總金額 4400 後,若要在結帳畫面顯示為 "4,400",可直接銜接 JS 數字加逗號與轉換千分位顯示。

3. 計算陣列平均值(Average)與空陣列除以零防呆

計算平均值的公式為「總和除以陣列長度(sum / arr.length)」。需特別注意當陣列為空(arr.length === 0)時,0 / 0 在 JavaScript 中會算出 NaN,因此必須先檢查陣列長度:

/**
 * 計算數字陣列的總和與平均值(自動略過 null、undefined 與非數字項目)
 * @param {Array} arr - 原始陣列
 * @returns {{ sum: number, average: number, validCount: number }}
 */
function getArrayStats(arr) {
  if (!Array.isArray(arr) || arr.length === 0) {
    return { sum: 0, average: 0, validCount: 0 };
  }

  let sum = 0;
  let validCount = 0;

  for (const item of arr) {
    // 只接受數字或非空白字串,避免 true、[]、' ' 被 Number() 轉成 1 或 0 混進計算
    const isNumberLike =
      typeof item === 'number' || (typeof item === 'string' && item.trim() !== '');
    if (isNumberLike && Number.isFinite(Number(item))) {
      sum += Number(item);
      validCount++;
    }
  }

  const average = validCount === 0 ? 0 : sum / validCount;
  return { sum, average, validCount };
}

const scores = [85, 92, '78', null, 95];
const stats = getArrayStats(scores);
console.log(stats);
// 輸出: { sum: 350, average: 87.5, validCount: 4 }

(註:若想事先將陣列中的 null 與空值清理乾淨,亦可參考 JS 去除 array 中 null 或空值;若平均值需要無條件進位,可搭配 JavaScript Math.ceil 無條件進位。)


三、 常見問題與排錯 Q&A

Q1:為什麼陣列加總結果突然變成一長串數字字串(如 "0100200300")?

  • 問題原因:陣列中的元素來自表單 <input> 或 URL 參數,型別實際上是字串(如 ["100", "200", "300"])。當初始值為數字 0,第一圈 0 + "100" 就會觸發字串串接變成 "0100"!
  • 解決思路:在累加時務必明確執行 acc + Number(cur) 將當前元素轉為數字型別。

Q2:在 React JSX 中可以直接渲染 items.reduce(...) 的結果嗎?

  • 問題原因:可以,只要確保 reduce 的初始值為 0 且內部做好 Number() 防呆,不會算出 NaN。
  • 解決思路:若 items 資料量較大且元件頻繁重新渲染,建議可搭配 useMemo 快取計算結果,或將格式化後的千分位字串(如 .toLocaleString())一併封裝輸出。

參考資料與延伸閱讀

code

發佈留言