シートの不揃い、どうしてます? — 「お手本シート」方式なら1枚直せば全部揃う

実際のビジネスシーンで使える自動化100例をGASで実装するシリーズ「GAS自働化100本ノック」。その記念すべき10本目

ヘッダー行の色・幅・フォントサイズがいつの間にかバラバラ

部署 A
部署 B
部署 C

複数の人や部署でスプレッドシートを使っていると、ヘッダー行の背景色、列幅、フォントサイズ、行固定のあるなし、などがシート間でバラバラってこと、あるあるですよね?

揃えても、また時間が経つとバラバラ…
直しても、また誰かが独自のシートを追加してる…
毎回、手作業で揃えるのは、典型的な「面倒案件」です。

GASでなんとかしましょう!

「お手本シート」方式でストレスフリー

GASなら、1枚の「お手本」シートを基準として、ボタン1つで他の全シートに ヘッダー行の書式・各列の列幅・固定行 をまとめてコピーできます。

GASでできること

  1. 「お手本」シートに整えたい体裁で1行目のヘッダーを書く

  2.  自動ボタンを押すだけ

  3.  お手本以外の全シートの1行目が、お手本と同じ書式・列幅・固定行になる

そのコードがこちら

コード.gs

// コード.gs
/**
 * お手本シートの書式を、他の全シートに一発で揃える。
 *   - 1行目(ヘッダー)の書式:背景色・文字色・太字・フォントサイズ・水平方向配置
 *   - 各列の列幅
 *   - フリーズ行(タイトル行固定)
 * メニュー「自動ボタン → お手本シートに合わせて全シートの書式を統一」から実行。
 */

const TEMPLATE_SHEET = 'お手本';

function unifyFormatFromTemplate() {
  const ui = SpreadsheetApp.getUi();
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const template = ss.getSheetByName(TEMPLATE_SHEET);
  if (!template) {
    ui.alert('「' + TEMPLATE_SHEET + '」シートが見つかりません。先に作成してください。');
    return;
  }

  const lastCol = template.getLastColumn();
  if (lastCol < 1) {
    ui.alert('「' + TEMPLATE_SHEET + '」の1行目に列見出しを書いてください。');
    return;
  }

  // お手本の1行目から書式を取得
  const headerRange = template.getRange(1, 1, 1, lastCol);
  const headerBg = headerRange.getBackgrounds()[0];
  const headerFontColor = headerRange.getFontColors()[0];
  const headerWeight = headerRange.getFontWeights()[0];
  const headerSize = headerRange.getFontSizes()[0];
  const headerAlign = headerRange.getHorizontalAlignments()[0];

  // 各列の列幅
  const widths = [];
  for (let c = 1; c <= lastCol; c++) widths.push(template.getColumnWidth(c));

  // フリーズ行
  const frozenRows = template.getFrozenRows();

  // 全シートに適用(お手本自身は除外)
  let count = 0;
  for (const sheet of ss.getSheets()) {
    if (sheet.getName() === TEMPLATE_SHEET) continue;

    const tgtHeader = sheet.getRange(1, 1, 1, lastCol);
    tgtHeader.setBackgrounds([headerBg]);
    tgtHeader.setFontColors([headerFontColor]);
    tgtHeader.setFontWeights([headerWeight]);
    tgtHeader.setFontSizes([headerSize]);
    tgtHeader.setHorizontalAlignments([headerAlign]);

    for (let c = 1; c <= lastCol; c++) sheet.setColumnWidth(c, widths[c - 1]);
    sheet.setFrozenRows(frozenRows);

    count++;
  }

  SpreadsheetApp.flush();
  ui.alert(count + ' 枚のシートに「' + TEMPLATE_SHEET + '」シートの書式を適用しました。');
}

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('自動ボタン')
    .addItem('お手本シートに合わせて全シートの書式を統一', 'unifyFormatFromTemplate')
    .addToUi();
}

使い方

① 新規スプレッドシートを用意
新規スプレッドシートで動作確認してから本番に導入するのがおすすめです。
 既存スプレッドシートに導入する場合:コピー版で試すと安全です。

② 「お手本」シートを作成
シートを1枚追加して名前を「お手本」にし、整えたい体裁で1行目のヘッダーを書きます。

お手本シートの例

記事では、上図のようなヘッダー行の体裁にしました。

  • 背景色・文字色・太字・フォントサイズ・水平方向配置

  • 各列の幅

  • 1行目固定

このお手本シートが、他の全シートの「正解」になります。

③ 拡張機能 → Apps Script にコード.gsを貼り付けて保存

④ スプレッドシートをリロード すると、メニューに「自動ボタン」が現れます。

初めて実行したときだけ承認ダイアログが表示されます(承認手順は第1回と同じ)。

⑤ メニュー「自動ボタン → お手本シートに合わせて全シートの書式を統一」を実行
実行直後、お手本以外の全シートの1行目が、お手本と同じ色・同じ太字・同じサイズ・同じ配置になります。
各シートの列幅もお手本と同じになり、1行目が固定された状態になります。

部署A,B,Cのヘッダーがお手本通りに揃った

不揃いのシートが増えたら、もう一度ボタンを押すだけでお手本通りに揃います。

ヘッダーの書式を変えたいなら、お手本シート1枚を直せば全部揃います!

ワンポイント・レッスン

`getSheets()` と `for…of` — 全シートを順番にまわす基本パターン

このスクリプトの心臓部は、`unifyFormatFromTemplate` 関数の中にある「お手本以外の全シートに書式を適用する」ループです。
「① 全シートの配列を取得 → ② `for…of` で1枚ずつ取り出す → ③ お手本シート自身は `continue` でスキップ」の3点だけで成り立っています。

① `ss.getSheets()` でシートの配列をまるごと取得する

スプレッドシート全体(`ss`)に対して `getSheets()` を呼ぶと、そのスプレッドシートの全シートが「配列」として返ります

const ss = SpreadsheetApp.getActiveSpreadsheet();
const allSheets = ss.getSheets();
// allSheets は [Sheet, Sheet, Sheet, ...] のような配列
// 順番はシートタブの並び順と同じ

「シート名のリスト」ではなく「シートオブジェクトのリスト」が返るので、配列から取り出した各要素に対していきなり `getName()` や `getRange()` などのシート操作メソッドが使えます。

② `for…of` で配列を1要素ずつ取り出す

`getSheets()` で取った配列を `for…of` でまわします。

for (const sheet of ss.getSheets()) {
  // ここで sheet には1枚のシートが入っている
  // ループの中身を書けば、それが全シートに対して順番に適用される
}

`for…of` は「配列の中身を1つずつ取り出す」ことに特化したループ構文です。
配列の長さを気にせず、`for (let i = 0; i < arr.length; i++)` のような添字を使うコードより意図がそのまま読めるのが利点です。
`sheet` という単数形の変数に1枚ずつシートが入る、と書いてある通りに動きます。

③ お手本シート自身は `continue` でスキップ

ループの先頭で「もしお手本シートだったら、このシートの処理は飛ばす」を書きます。

for (const sheet of ss.getSheets()) {
  if (sheet.getName() === TEMPLATE_SHEET) continue;
  // ここから先が「お手本以外」の処理
  const tgtHeader = sheet.getRange(1, 1, 1, lastCol);
  tgtHeader.setBackgrounds([headerBg]);
  // ...
}

`continue` は「このループ周はここで終わり、次の要素に進む」という命令です。お手本に書式を適用すると、自分自身の書式を自分自身で上書きすることになって意味がないので、最初に弾いておきます。

つまり「配列をまるごと取る → 1枚ずつ取り出す → 当該シートだけ除外する」という3ステップだけで、何枚シートがあっても、シートが増えても、同じコードがそのまま動きます。
今後シートを横断する処理(複数シートの一括チェックや、シートごとのまとめ集計など)を書くときも、この `getSheets() + for…of + continue` の組み合わせがそのまま使えます。

まとめ

「お手本」シート1枚を基準として、他の全シートのヘッダー書式・列幅・固定行をボタン1つで統一します。
シートが追加されるたびに何度でも実行できるので、月末の体裁揃えが「シートを足してボタンを押すだけ」に変わります。


でも、ちょっと待って。

部署Cの「注文No」と「担当者」列の幅が広すぎるんだけど…!

はい。そうなんです!
細かいところによく気づきました!

コード.gsは「お手本のA列の書式 → 他シートのA列に適用、お手本のB列 → 他のB列に…」と 列番号で適用 します。
お手本も他シートも列の並び順が同じなら問題ないのですが、異なると部署Cのようになってしまいます。

ということで、
この問題を拡張コードでクリアしましょう!

拡張コードでできること

  • 「フォーマット定義」方式 —「フォーマット定義」シートに列名ごとに書式を書いておくと、列名が一致した列にだけ書式が当たる

    この記事の続きはnoteで公開しています。

    noteで続きを読む

GAS,自動化自動化

Posted by botw