Google 試算表:取得工作表數量 getNumSheets()

 


        如果你經常用 Google 試算表做報表,應該遇過這種情況:頁籤越長越多,命名亂成一團,腳本插錯位置、重複新增,最後只好手動救火。其實很多問題,在一開始先把「張數」數清楚就能避開。

        getNumSheets() 就是這把小扳手:回傳整本活頁簿目前有幾張工作表,給你一個可靠的數字去控制流程。

        本篇文章會用淺白的步驟跟範例,示範如何用它安排插入順序、做每月自動補分頁、寫儀表板統計,以及在門檻超過時自動停手。不多說專有名詞,重點放在可複製的寫法、錯誤排查的方法和幾個常見地雷。把這招放進你的腳本,報表流程會安靜很多。希望本篇文章可以幫助到需要的您。


目錄

{tocify} $title={目錄} 


為什麼要數「工作表」這件小事?

很多人寫報表自動化時,只盯著儲存格的值,忽略「工作表的數量」這個關鍵狀態。實務上你常會遇到:每天自動建立一張新頁籤做日誌、以「月份」當分頁整理數據、或在匯入資料後要把新分頁排到指定順序。這些都離不開先知道目前到底有幾張工作表。

在 Google Apps Script 裡,Spreadsheet 類別提供的 getNumSheets() 就是最俐落的解法:回傳整份活頁簿的分頁數量(整數)。這個方法穩、簡單、可讀性高,拿來做索引邏輯、排版、迴圈控制都很合適(官方參考頁清楚載明此方法會回傳分頁數量)。


getNumSheets() 是什麼?

定義:回傳目前活頁簿(Spreadsheet)裡工作表的總數(整數)。

所屬類別:Spreadsheet(從 SpreadsheetApp.getActiveSpreadsheet() 或 SpreadsheetApp.openById() 等取得)。


什麼情境會用到?

    1.    動態插入新分頁到最後:插入新表時需要用「目前表數」當作插入位置索引。

    2.    批次巡覽/稽核:跑 for 迴圈時決定上限,避免硬編寫死值。

    3.    自動命名/自動編號:例如建立 Report-#,# 以現有張數+1 產生。

    4.    資料清理:檢查是否超過允許的分頁上限,超過就合併或封存。

    5.    報表健檢:理應只有固定分頁數,異常增加代表流程有問題。

    6.    備援策略:搬移或重建分頁前確認數量,降低誤刪與漏刪風險。


快速上手:3 個步驟

1.    打開 Apps Script 編輯器

    在你的試算表中:擴充功能 → Apps Script,新建專案。

2.    取得活頁簿物件

const ss = SpreadsheetApp.getActiveSpreadsheet();


2.    取得活頁簿物件

const count = ss.getNumSheets(); // 例如回傳 7
Logger.log(`目前有 ${count} 張工作表`);

官方 Spreadsheet 類別文件載明 getNumSheets() 回傳整數。


十個實戰範例



1.    取得分頁數並寫到儀表板

function writeSheetCount() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const dashboard = ss.getSheetByName('Dashboard') || ss.insertSheet('Dashboard');
  const count = ss.getNumSheets();
  dashboard.getRange('B2').setValue(count);
}



2.    插入新分頁到最後(以目前張數為索引)

function insertAtEnd(name) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const index = ss.getNumSheets(); // 最後一個之後的位置
  ss.insertSheet(name, index);
}

社群範例中常見把 getNumSheets() 用來決定 insertSheet 的插入位置。


3.    建立流水命名的分頁(Report-001…)

function createSequentialReport() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const n  = ss.getNumSheets() + 1;
  const name = `Report-${String(n).padStart(3, '0')}`;
  ss.insertSheet(name, ss.getNumSheets());
}

4.    以檔案 ID 讀取(非目前活頁簿)

function countSheetsById(fileId) {
  const ss = SpreadsheetApp.openById(fileId);
  return ss.getNumSheets();
}

SpreadsheetApp 提供 openById() / openByUrl() 讀取其他檔案,再呼叫 getNumSheets()。


5.    每月自動補一張分頁:只在需要時建立

function ensureMonthlySheet() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const ym = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM');
  if (!ss.getSheetByName(ym)) {
    ss.insertSheet(ym, ss.getNumSheets());
  }
}

6.    巡覽全部分頁:以張數當上限(示範健檢)

function auditAllSheets() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const total = ss.getNumSheets();
  const sheets = ss.getSheets(); // 也可以直接用陣列長度
  for (let i = 0; i < total; i++) {
    const sh = sheets[i];
    // 你的檢查邏輯...
  }
}

7.    超過上限就封存:避免失控增生

function capSheetCount(limit = 24) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  let total = ss.getNumSheets();
  if (total <= limit) return;

  const archive = ss.getSheetByName('_archive') || ss.insertSheet('_archive', ss.getNumSheets());
  const sheets = ss.getSheets();
  // 把最舊的幾張移到封存(示意:從前面開始)
  while (total > limit) {
    const victim = sheets[0];
    if (victim.getName() === '_archive') break;
    victim.copyTo(archive).setName(`backup_${victim.getName()}`);
    ss.deleteSheet(victim);
    total--;
  }
}

8.    產生目錄頁:列出總數與清單

function buildTOC() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const toc = ss.getSheetByName('TOC') || ss.insertSheet('TOC');
  toc.clear();
  const sheets = ss.getSheets();
  toc.getRange(1,1).setValue(`Total: ${ss.getNumSheets()}`);
  for (let i = 0; i < sheets.length; i++) {
    toc.getRange(i+2, 1).setValue(sheets[i].getName());
  }
}

9.    自訂函數:在儲存格裡顯示分頁數

/**
 * 在儲存格輸入 =COUNT_SHEETS() 顯示分頁數
 */
function COUNT_SHEETS() {
  return SpreadsheetApp.getActiveSpreadsheet().getNumSheets();
}

10.    與流程控制結合(門檻判斷)

function guardBeforeImport(limit = 50) {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const total = ss.getNumSheets();
  if (total >= limit) {
    throw new Error(`分頁已達 ${total} 張,超過上限 ${limit},停止新增。`);
  }
  // 繼續你的匯入流程...
}

常見錯誤與雷點

1. 把 Spreadsheet 和 Sheet 搞混

    症狀:對 Sheet 物件呼叫 getNumSheets() 當然爆錯。

    修法:先取得 Spreadsheet(活頁簿)再呼叫。
const ss = SpreadsheetApp.getActiveSpreadsheet(); // 正確
// const sh = ss.getActiveSheet(); // 這是 Sheet,不是 Spreadsheet
ss.getNumSheets(); // 只能在 Spreadsheet 上呼叫

    文件對象:Spreadsheet、Sheet 類別分別定義於 Spreadsheet 服務。

2. 用錯環境/檔案目標

    症狀:你以為在操作某檔案,其實 getActiveSpreadsheet() 指到目前開啟的那一份。

    修法:要操作別的檔案,用 openById() 或 openByUrl() 明確指定。

3. 取數量後索引錯位

    症狀:插入位置 off-by-one。

    觀念:插入索引是從 0 開始(第 1 張是 index 0)。要放到最後,用 getNumSheets() 作為插入索引即可(相當於「尾端之後」)。

4. 以為「隱藏分頁不算」

    事實:隱藏分頁依舊是分頁,會被計入 getNumSheets()。

    建議:若你只想統計特定命名規則,可改用 getSheets() 後自行過濾。

5. 權限與共享雲端硬碟

    狀況:讀別人的檔案或共享雲端硬碟檔案,腳本可能需要授權;getOwner() 在共享雲端硬碟可能回傳 null,但不影響 getNumSheets()。

6. 巨量分頁導致速度慢

    症狀:上百張分頁時,後續巡覽或寫入變慢。

    作法:

            盡量一次取得陣列 const sheets = ss.getSheets() 再操作。

            降低 API 呼叫次數;把屬性讀出後在記憶體處理。

            大量更新前後可搭配 SpreadsheetApp.flush() 控制時機。


7. 逾時(Timeout)誤判

    狀況:
            有時是你後續對 getSheetByName() 或存取範圍的操作卡住,而不是 getNumSheets() 本身。

    排查:
            逐行加 Logger.log、分段註解,找出卡點;社群也有人回報過 getSheetByName() 偶發卡住的案例。


8. 插入循環 + 事件觸發器的副作用

    狀況:onEdit/onChange 觸發器與自動插入新分頁互相觸發,導致暴增。

    解法:加鎖、加旗標欄位或在觸發器一開始檢查 e.changeType 後再決定是否執行。

9. 自訂函數濫用

    狀況:COUNT_SHEETS() 放太多儲存格,導致工作表每次重算都觸發 Apps Script。

    建議:改寫成按鈕(指令選單)或排程每日一次寫入數值。

10. 舊專案與新編輯器差異

    狀況:舊專案搬到新版 Apps Script 編輯器後,授權/時區設定不同步。

    建議:檢查 專案設定 → 時區、重新授權一次,並統一使用 Session.getScriptTimeZone()。


問題集

Q1:隱藏的分頁會算進去嗎?
    會。getNumSheets()看的是活頁簿的分頁總量,不理會是否隱藏。

Q2:我能只數名稱符合某條件的分頁嗎?
    原生沒有條件參數。做法是 getSheets() 取回陣列後 filter 計數。

Q3:要操作別的試算表怎麼數?
    用 SpreadsheetApp.openById() 或 openByUrl() 取得目標,再 getNumSheets()。

Q4:有內建公式能在儲存格直接顯示分頁數嗎?
沒有;需用 Apps Script 自訂函數。社群討論也常用這招。



張貼留言 (0)
較新的 較舊