Google 試算表:取得Column 欄的編號 getColumn()

 


你可能也有這種經驗:報表做好了,要寫一段腳本清整欄資料,但欄位常常會換位置;手動改範圍很煩,也很容易出錯。

getColumn() 的價值就在這裡——它把你選到的「那一欄」變成清楚的欄號(A=1、B=2…),讓後續的 getRange(row, column, ...)、getValues()、setValues() 都能精準落點。

本文用淺白步驟拆解實作:如何從使用者選取取得欄號、怎麼只處理內容不動表頭、如何把欄號換成 A1(像 C:C)做記錄或提示。

也會示範幾個常用場景:依標題找欄再加工、選到哪欄就套資料驗證、一次處理多欄但以左上角為基準。最後整理效能與維護建議,以及最容易踩的坑,幫你把腳本寫得穩、快、好改。希望本篇文章能夠幫助到需要的您。


目錄

{tocify} $title={目錄} 


getColumn() 是什麼?


所屬類別:Range(Apps Script 的 SpreadsheetApp 服務)

語法:range.getColumn() : Integer

回傳值:整數欄號,從 1 開始(A 欄 = 1、B 欄 = 2、…)

重點:如果 range 是多格範圍,它回傳的是左上角那格的欄號,而不是「涵蓋的所有欄」。

小提醒:getColumn() 回傳數字;若你想要欄字母(A、B、C…),需要自己轉換,下面會給你現成工具函式。


為什麼要用 getColumn()?常見情境


1.    根據使用者選取的儲存格,定位要處理的整欄資料
例如:使用者點哪欄,你就批次清洗那欄。

2.    根據標題列動態尋找欄位
例如:找到「Email」標題在哪欄,之後所有讀寫都靠欄號運作。

3.    將欄號換成 A1 標記做輸出或記錄
Log、訊息或 UI 上常需要 A、B、C 這種標記。

4.    配合 getRange(row, column, numRows, numColumns) 做批次讀寫
以欄號為基準挑選一整欄,結合 getValues()/setValues() 一次處理。


快速上手:最小可行範例

拿到目前使用者游標所在欄號

function logActiveColumn() {
  const ss = SpreadsheetApp.getActive();
  const sheet = ss.getActiveSheet();
  const range = sheet.getActiveRange(); // 使用者目前選到的格子/範圍
  if (!range) {
    Logger.log('沒有選取任何範圍');
    return;
  }
  const col = range.getColumn();
  Logger.log(`你現在在第 ${col} 欄`);
}

以選取欄為基準,讀取整欄資料

function readWholeActiveColumn() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const active = sheet.getActiveRange();
  const col = active.getColumn();
  const lastRow = sheet.getLastRow(); // 試算表目前有資料的最後一列
  if (lastRow < 1) return; // 空表直接結束
  const colRange = sheet.getRange(1, col, lastRow, 1);
  const values = colRange.getValues(); // 取得整欄(到最後有資料的列)
  Logger.log(JSON.stringify(values));
}

轉成 A1 欄字母(給提示訊息或寫入紀錄)

function columnNumberToLetter(n) {
  // 1 -> A, 2 -> B, 26 -> Z, 27 -> AA ...
  let s = '';
  while (n > 0) {
    const m = (n - 1) % 26;
    s = String.fromCharCode(65 + m) + s;
    n = Math.floor((n - 1) / 26);
  }
  return s;
}

function showActiveColumnLetter() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const col = sheet.getActiveRange().getColumn();
  const letter = columnNumberToLetter(col);
  SpreadsheetApp.getUi().alert(`你在第 ${letter} 欄`);
}


逐步操作:從「選取 → 取欄 → 批次處理」


1.    取得活頁簿與工作表

const ss = SpreadsheetApp.getActive();
const sheet = ss.getActiveSheet();


2.    取得使用者選取範圍(或指定範圍)

const range = sheet.getActiveRange(); // 或 sheet.getRange('C2:D10')


3.    用 getColumn() 取基準欄

const baseCol = range.getColumn(); // 左上角那格的欄號


4.    決定處理範圍(整欄/部分列)

const lastRow = sheet.getLastRow();
const target = sheet.getRange(2, baseCol, Math.max(lastRow - 1, 0), 1); // 跳過標題列


5.    一次讀值、一次寫回(避免逐格存取)

const data = target.getValues(); // [[val],[val],...]
const cleaned = data.map(([v]) => [typeof v === 'string' ? v.trim() : v]);
target.setValues(cleaned);


6.    可選:給個視覺化回饋

sheet.getRange(1, baseCol, lastRow || 1, 1).setBackground('#FFF2CC'); // 淡黃色標記整欄


整段組合起來:

function cleanActiveColumnTrim() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const range = sheet.getActiveRange();
  if (!range) return;

  const col = range.getColumn();
  const lastRow = sheet.getLastRow();
  if (lastRow <= 1) return; // 只有標題或空表就跳過

  const bodyRange = sheet.getRange(2, col, lastRow - 1, 1);
  const values = bodyRange.getValues();
  const output = values.map(([v]) => [typeof v === 'string' ? v.trim() : v]);

  bodyRange.setValues(output);
  sheet.getRange(1, col, lastRow, 1).setBackground('#FFF2CC');
}


實務情境範例

範例 A:依標題找欄,再批次處理

需求:標題列(第 1 列)有很多欄,我要找到「Email」那欄,對它做去空白、轉小寫。
function processEmailColumn() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const header = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; // 取第一列
  const targetIdx = header.indexOf('Email');
  if (targetIdx === -1) {
    SpreadsheetApp.getUi().alert('找不到 Email 欄');
    return;
  }
  const col = targetIdx + 1; // index 從 0 起算,要 +1 變 1-based 欄號
  const lastRow = sheet.getLastRow();
  if (lastRow <= 1) return;

  const dataRange = sheet.getRange(2, col, lastRow - 1, 1);
  const data = dataRange.getValues();
  const updated = data.map(([v]) => [ typeof v === 'string' ? v.trim().toLowerCase() : v ]);
  dataRange.setValues(updated);
}


重點:找標題用陣列的 indexOf,得到 0-based 索引;轉欄號要 +1。這就是 getColumn() 回傳 1-based 的典型整合情境。



範例 B:使用者選到哪欄,就幫他建立資料驗證

需求:點某欄 → 套用「只能輸入 Email 格式」的資料驗證。

function applyEmailValidationToActiveColumn() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const col = sheet.getActiveRange().getColumn();
  const lastRow = Math.max(sheet.getMaxRows(), sheet.getLastRow() || 1);
  const range = sheet.getRange(2, col, lastRow - 1, 1); // 從第2列到底

  const rule = SpreadsheetApp.newDataValidation()
    .requireTextIsEmail()
    .setAllowInvalid(false)
    .setHelpText('請輸入有效的 Email')
    .build();

  range.setDataValidation(rule);
}



範例 C:選取多欄時,僅以「左上角」為基準

需求:使用者可能框了 C2:E10,但我只想以 C 欄 為主做處理。

function processByLeftTopOnly() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const sel = sheet.getActiveRange(); // 可能是多欄
  const baseCol = sel.getColumn(); // 只取左上角的欄
  const lastRow = sheet.getLastRow();
  if (lastRow < 2) return;

  const rng = sheet.getRange(2, baseCol, lastRow - 1, 1); // 只處理 baseCol 這一欄
  // ...你的處理邏輯
}


範例 D:欄號 ↔ 欄字母互轉工具組(常年用得到)

function columnNumberToLetter(n) {
  let s = '';
  while (n > 0) {
    const m = (n - 1) % 26;
    s = String.fromCharCode(65 + m) + s;
    n = Math.floor((n - 1) / 26);
  }
  return s;
}

function columnLetterToNumber(letter) {
  let n = 0;
  letter = String(letter).toUpperCase().trim();
  for (let i = 0; i < letter.length; i++) {
    n = n * 26 + (letter.charCodeAt(i) - 64);
  }
  return n;
}


getColumn() 與其他常用方法的關係


getRow():回傳左上角儲存格的列號(也是 1-based)。通常搭配 getColumn() 一起定位。

getLastColumn()(Sheet 級):回傳工作表「目前有資料」的最後一欄號。與 getColumn() 不同,它是看整張表的資料分布。

getMaxColumns()(Sheet 級):工作表總欄數(包含空欄)。

getRange(row, column, numRows, numColumns):拿到欄號之後,真正好用的是它。用欄號當座標,配合 getValues()/setValues() 一次性批次處理。


效能與可維護性:寫穩、寫快、寫久


1.    一次取值、一次寫回(避免迴圈逐格)
    getValues() / setValues() 批次處理 → 比 .getValue()/.setValue() 逐格快得多。

2.    盡量用欄號運算,不用字串拼 A1 
    A1 標記可讀性高,但在程式邏輯與抽換欄位時,欄號更穩定。必要時再轉 A1 給使用者看。

3.    表頭與內容分層 
    習慣將第 1 列當標題,資料從第 2 列起。寫 getRange(2, col, lastRow-1, 1) 的邏輯更直觀。

4.    保護範圍與合併儲存格預檢 
    在大量改資料前,檢查 range.isPartOfMerge() 或先取消合併,避免拋錯。

5.    避免在 onEdit 內做大量 I/O 
    onEdit 是使用者每次輸入都會觸發,易卡頓。把重活放到選單或按鈕觸發的函式。


常見錯誤與雷點(含解法)


1. 把 1-based 當 0-based 用 

    症狀:欄位偏移、讀錯欄。

    說明:Apps Script 的 getColumn() 回傳 1 起算。

    解法:陣列索引(0-based)轉欄號要 +1;從欄號回陣列索引要 -1。


2. 以為 getColumn() 會回全範圍的所有欄 

    症狀:以為選到 C:E,就會拿到 [3,4,5]。

    說明:它只回「左上角那格」的欄號。

    解法:若你要所有欄,請用 range.getNumColumns() 搭配起始欄自己推算,或用 getA1Notation() 分析。


3. A1 與欄號混用出錯 

    症狀:getRange('A', col) 之類無效語法。

    說明:A1 模式與 R1C1(數字座標)是兩種不同呼叫。

    解法:要嘛全 A1(字串),要嘛全數字(row/column)。不要混搭。


4. 合併儲存格導致範圍與欄界不一致 

    症狀:setValues() 報大小不符,或處理到不該動的格。

    解法:先 range.breakApart() 或避免在合併的區段批次寫入。


5. 忽略保護範圍(Protected Range)

    症狀:腳本在某欄寫入時拋錯或無效。

    解法:檢查是否有保護,必要時調整權限或改寫邏輯到允許寫入的欄位。


6. 用 getLastColumn() 以為就能讀到全部 

    症狀:尾端有空白但其實你還想讀固定欄位的資料,結果被 lastColumn 影響。

    解法:明確用 getColumn() + 固定欄距計算你的目標,而不是跟著 lastColumn 走。


7. 逐格寫入造成超慢 

    症狀:資料一多整個卡住。

    解法:陣列化,一次 setValues()。


8. 觸發器(Triggers)裡大量 UI 互動 

    症狀:SpreadsheetApp.getUi().alert 在觸發器下不 work 或卡住。

    解法:觸發器內避免 UI;寫到記錄欄、Log,或用側邊欄/選單手動觸發的流程。



問題集


Q1:我可以對「多欄選取」一次拿到所有欄號嗎?
A:getColumn() 只回左上角欄號。若要所有欄,請用:

const startCol = range.getColumn();
const numCols = range.getNumColumns();
const allCols = Array.from({length: numCols}, (_, i) => startCol + i);


Q2:我要把欄號變成 A1 的「整欄」表示?
A:例如第 3 欄整欄,可用 C:C。你可以:

const letter = columnNumberToLetter(3);
const wholeColumnA1 = `${letter}:${letter}`; // "C:C"


Q3:合併儲存格會影響 getColumn() 嗎?
A:getColumn() 仍回左上角欄號,但你在寫入時可能會被合併範圍擋住或尺寸不匹配。實務上先 breakApart() 最穩。

Q4:getColumn() 跟 getA1Notation() 要用哪個?
A:寫程式邏輯建議用欄號(數字),對人顯示(Log、UI)再轉 A1,兩者各司其職最不容易亂。




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