シートを操作したい(Sheet)

1const book = SpreadsheetApp.openById("スプレッドシートのID");
2const sheet = book.getSheetByName("シート名");

Sheetオブジェクトで単一のシートを操作できます。 ブックの取得方法についてはブックを操作したいを参照してください。

シートのIDと名前を確認したい(getId / getName)

1const id = sheet.getId();
2const name = sheet.getName();

Sheet.getIdでシートのID(ブック内でシートを一意に識別する数値)を、 Sheet.getNameでシート名を取得できます。

ヒント

Sheet.getSheetNameという同名のメソッドもありますが、 getNameと同じ値を返す旧API互換のためのエイリアスです。 新しく書く場合はgetNameを使えばよいです。

シートの位置を確認したい(getIndex)

1const index = sheet.getIndex();

Sheet.getIndexで、ブック内でのシートの位置(1はじまり)を取得できます。

最終行・最終列を確認したい(getLastRow / getLastColumn)

1const lastRow = sheet.getLastRow();
2const lastCol = sheet.getLastColumn();

Sheet.getLastRowでデータが入っている最終行の行番号を、 Sheet.getLastColumnでデータが入っている最終列の列番号を取得できます。 どちらもデータが1件もない場合は0が返ります。

行を操作したい(appendRow / deleteRow)

1// データのカラム数と同じ要素の配列を作成
2const data = ["A", "B", "C", "D"];
3// データをシート末尾に追記
4sheet.appendRow(data);
5
6// 2行目を削除
7sheet.deleteRow(2);

Sheet.appendRowで既存のシート末尾にデータを追加できます。 Sheet.deleteRowで行番号を指定して行ごと削除できます。

列を操作したい(insertColumnBefore / insertColumnAfter / deleteColumn)

1// 3列目の前に列を追加
2sheet.insertColumnBefore(3);
3
4// 3列目の後に列を追加
5sheet.insertColumnAfter(3);
6
7// 3列目を削除
8sheet.deleteColumn(3);

Sheet.insertColumnBefore、Sheet.insertColumnAfterで、指定した列番号の前後に新しい列を追加できます。 Sheet.deleteColumnで、指定した列番号の列を削除できます。

1const values = sheet.getDataRange().getValues();
2const headers = values[0];
3const columnIdx = headers.indexOf("名前") + 1;
4sheet.insertColumnBefore(columnIdx);

データを全選択したい(getDataRange)

1// すべてのデータの範囲を選択
2const range = sheet.getDataRange();
3// 2次元配列として取得
4const values = range.getValues();
5
6// データを見出し(headers)とデータ行(rows)に分割
7const headers = values[0];
8const rows = values.slice(1);

Sheet.getDataRangeで、シートにあるデータを全選択できます。 余計な空行・空列は含まれないので、シート全体でデータを管理している場合によく使います。 Range.getValuesをつなげることで、選択した範囲を2次元配列として取得できます。

1行目が見出しになっている場合は、上記のようにheaders(見出し)とrows(データ行)に分割しておくと、 以降の処理で扱いやすくなります。 2次元配列の詳しい操作方法(行・列・カラム名でのアクセスなど)は配列したいの「2次元配列したい」を参照してください。

データを範囲選択したい(getRange)

1// A1表記で指定
2const range = sheet.getRange("A1:B3");
3
4// 行番号/列番号で指定(開始行, 開始列, 行数, 列数)
5const range = sheet.getRange(2, 1, 3, 2);
6
7// R1C1形式でも指定できる
8const range = sheet.getRange("R2C1:R4C2");

Sheet.getRangeでセル番地(や行番号/列番号)を指定して、任意の範囲を選択できます。 セル番地は大文字でも小文字でもOKです。

1// 末尾の空き行を、2次元配列として取得
2const lastRow = sheet.getLastRow();
3const range = sheet.getRange(lastRow + 1, 1);
4const values = range.getValues();

データを読み込むときより、既存のデータを追記したいときに利用します。

いずれもRangeオブジェクトが返るので、 値の読み書きや書式変更などの詳しい操作はセルを操作したいを参照してください。

シートを複製したい(copyTo)

1// 同一ブック内で複製
2const sourceBook = SpreadsheetApp.openById("コピー元のID");
3const sourceSheet = sourceBook.getSheetByName("複製したいシート名");
4const copiedSheet = sourceSheet.copyTo(sourceBook);

Sheet.copyToで、指定したSpreadsheet(ブック)にシートを複製できます。 コピー元と同じブックを渡せば、同一ブック内での複製になります。

複製されたシートの名前は「シート名のコピー」のように自動で付けられるため、 必要であれば次のSheet.setNameで変更してください。

1// 別のブックにコピー
2const sourceBook = SpreadsheetApp.openById("コピー元のID");
3const sourceSheet = sourceBook.getSheetByName("複製したいシート名");
4const targetBook = SpreadsheetApp.openById("コピー先のID");
5const copiedSheet = sourceSheet.copyTo(targetBook);

コピー先のSpreadsheet(ブック)を指定すれば、別のブックにシートをコピーすることもできます。

シート名を変更したい(setName)

1sheet.setName("変更後のシート名");

Sheet.setNameでシート名を変更できます。 同じ名前のシートは作れません。

シートを保護したい(protect)

1// シート全体を保護
2const protection = sheet.protect();
3
4// 保護の理由を追加
5protection.setDescription("説明");

Sheet.protectでシート全体を保護できます。 Protection.setDescriptionで保護の理由を追加できます。 セル範囲だけを保護したい場合はセルを操作したいを参照してください。

ヘッダー行を取得したい(getHeaders)

シート操作は、基本的にカラム番号(1はじまり)が前提となっていますが、カラム追加や順番の変更に弱いです。 ヘッダー行をMap型(Map<string, number>)に変換しておくことで、 カラム名から安全かつ可読性の高い形でカラム番号を取得できるようにします。

 1function getHeaders(
 2  sheet: GoogleAppsScript.Spreadsheet.Sheet
 3): Map<string, number> {
 4  const headers = sheet
 5    .getRange(1, 1, 1, sheet.getLastColumn())
 6    .getValues()[0];
 7
 8  const map = new Map<string, number>();
 9
10  headers.forEach((h, i) => {
11    const key = String(h).trim();
12    if (key) {
13      map.set(key, i + 1);
14    }
15  });
16  return map;
17}
18
19const headers = getHeaders(sheet);

getHeadersで、1行目(見出し行)をカラム名 -> カラム番号のMapに変換します。

カラム名からカラム番号を取得したい(getColumnIndex)

 1function getColumnIndex(
 2  headers: Map<string, number>,
 3  name: string
 4): number {
 5  const index = headers.get(name.trim());
 6  if (index === undefined) {
 7    throw new Error(`Column "${name}" not found`);
 8  }
 9  return index;
10}
11
12// Usage
13const headers = getHeaders(sheet);
14const nameColIndex = getColumnIndex(headers, "名前");

getColumnIndexで、getHeadersが返したMapからカラム名を指定してカラム番号を取得します。 カラム名で操作できるようになるので、シートのカラム構成の変更にも強くなります。

行を安全に追加したい(appendRowSafe)

appendRowSafeは、追加する行のカラム数がシートの列数とズレていないかをチェックする自作関数です。

 1type Cell = string | number | boolean | Date | null;
 2type Row = Cell[];
 3
 4function appendRowSafe(
 5  sheet: GoogleAppsScript.Spreadsheet.Sheet,
 6  row: Row
 7) {
 8  const width = sheet.getLastColumn();
 9  if (width !== 0 && row.length !== width) {
10    throw new Error("列数が一致していません");
11  }
12
13  sheet.appendRow(row);
14}
15
16const row: Row = ["A", 123, true, new Date(), null];
17appendRowSafe(sheet, row);

Sheet.appendRowをそのまま呼ぶだけでは、渡した配列の要素数がシートの列数とズレていてもエラーにならず、ズレたままデータが書き込まれてしまいます。 列数が一致しない場合にthrowするappendRowSafeでラップしておくと、このミスに早い段階で気づけます。

ヒント

SpreadsheetApp.openByIdなどでシートを取得する処理は時間がかかります。 ラッパー関数の中で毎回呼び出すのではなく、 あらかじめ取得したシートを引数として渡すとよいです。

複数行を安全に追加したい(appendRowsSafe)

appendRowsSafeは、複数行の列数がすべて揃っているかをチェックしたうえで、1回のAPI呼び出しでまとめて追加する自作関数です。

 1type Cell = string | number | boolean | Date | null;
 2type Row = Cell[];
 3
 4function appendRowsSafe(
 5  sheet: GoogleAppsScript.Spreadsheet.Sheet,
 6  rows: Row[]
 7) {
 8  if (rows.length === 0) return;
 9
10  const width = rows[0].length;
11
12  // すべての行のカラム数をチェック
13  if (!rows.every(row => row.length === width)) {
14    throw new Error("すべての行の列数が一致していません");
15  }
16
17  sheet
18    .getRange(sheet.getLastRow() + 1, 1, rows.length, width)
19    .setValues(rows);
20}
21
22const rows: Row[] = [
23  ["A", "B"],
24  [1, 2],
25];
26appendRowsSafe(sheet, rows);

Sheet.appendRowを行数分ループで呼び出すと、その都度APIが呼ばれるため時間がかかります。 大量のデータを追加する場合は、2次元配列にまとめてからRange.setValuesで一括書き込みしたほうが高速です。

データの重複を探したい(findDuplicateRow)

findDuplicateRowは、指定したカラムの値の組み合わせが一致する行を探し、見つかった行番号(見つからなければ-1)を返す自作関数です。 「名前」と「メールアドレス」の組み合わせで重複を確認する、といった用途に使えます。

 1type Cell = string | number | boolean | Date | null;
 2type Row = Cell[];
 3
 4function toKey(v: Cell): string {
 5  if (v instanceof Date) return String(v.getTime());
 6  if (v === null) return "null";
 7  return String(v);
 8}
 9
10function findDuplicateRow(
11  sheet: GoogleAppsScript.Spreadsheet.Sheet,
12  colIndices: number[],
13  values: Cell[],
14  excludeRowIndex?: number
15): number {
16  if (colIndices.length != values.length) {
17    throw new Error("Length doesn't match: colIndices and values");
18  }
19
20  const lastRow = sheet.getLastRow();
21  if (lastRow < 2) return -1;
22
23  const maxCol = Math.max(...colIndices);
24
25  const rows = sheet
26    .getRange(2, 1, lastRow - 1, maxCol)
27    .getValues() as Cell[][];
28
29  for (let i = 0; i < rows.length; i++) {
30    const rowIndex = i + 2;
31    if (rowIndex === excludeRowIndex) continue;
32
33    const row = rows[i];
34
35    const isMatch = colIndices.every(
36      (col, j) => toKey(row[col - 1]) === toKey(values[j])
37    );
38    if (isMatch) return rowIndex;
39  }
40  return -1;
41}
42
43// Usage
44// 2列目(名前)と3列目(メールアドレス)の組み合わせが
45// ["田中", "tanaka@example.com"]と一致する行を探す
46const rowIndex = findDuplicateRow(sheet, [2, 3], ["田中", "tanaka@example.com"]);

colIndicesとvaluesは同じ順番で対応させます(colIndices[i]列目の値がvalues[i]と一致するか、をすべてのiについて確認します)。 Date型はそのまま比較できないため、toKeyで文字列に変換してから比較しています。 更新時など、自分自身の行を重複判定から除外したい場合はexcludeRowIndexに行番号を渡してください。

リファレンス