你可能也有這種經驗:報表做好了,要寫一段腳本清整欄資料,但欄位常常會換位置;手動改範圍很煩,也很容易出錯。
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,兩者各司其職最不容易亂。
