凛です。うちの家では、買い物リストも子どもの持ち物準備も、ぜんぶスプレッドシートで管理しています。夜勤明けのぼんやりした頭でも、チェックを入れるだけで「終わったこと」が消えていったら気持ちいいのに…と、ずっと思っていました。実際、終わったタスクをいちいち手で切り取って別のシートに貼り付けるのが地味に面倒で、そのうち面倒くさくなって放置→リストがぐちゃぐちゃ、というのを何度もやらかしてきました。
そこで、チェックボックスにチェックを入れたら、その行がスッと「完了」シートに移動してくれる仕組みをGAS(Google Apps Script)で作ってみたら、これが想像以上に快適だったんです。今回は、私が実際に組んで使っている「チェックした行を自動で別シートへ移す」仕組みを、コピペで動くコード付きで、最初から順番に紹介していきます。
GASでチェックを入れた行を別シートへ自動で移す
スプレッドシートには「onEdit」という便利な仕組みがあって、セルを編集した瞬間に自動でスクリプトを走らせることができます。これを使えば、「チェックボックスにチェックが入った→その行を完了シートに移す」という動作を、ボタンも押さずに全自動でやってくれます。プログラミングが初めての方でも、手順どおりに進めれば必ず動かせるように書いていきますね。
そもそも何を作るのか(完成イメージ)
まずはゴールを共有させてください。作るのは、次のような動きをするスプレッドシートです。
動きのイメージ
「TODO」というシートにやることリストを並べておきます。各行の一番右にチェックボックスを置いておいて、タスクが終わったらそこにチェックを入れます。すると、その行がまるごと「完了」シートへ移動して、TODOシートからは消える、という動きです。
手で切り取り→貼り付け→元の行を削除、という3ステップを、チェック1回に置き換えられるイメージです。夜勤明けのぼんやり頭でも、チェックを入れるだけなら間違えません。
用意するシートは2枚
今回は次の2枚のシートを使います。名前は自由に変えられますが、まずはこの名前で進めるのがおすすめです。
| シート名 | 役割 |
|---|---|
| TODO | やることリスト(チェックを入れる元のシート) |
| 完了 | チェックが入った行が自動で移動してくる先 |
TODOシートには、A列に「やること」、B列に「メモ」…といった感じで自由に列を作ってOKです。今回はE列(5列目)にチェックボックスを置く前提で進めますが、あとで自分の環境に合わせて変えられます。
使う技術は「シンプルトリガー onEdit」
GASには「トリガー」という、特定のタイミングで自動的にスクリプトを動かす仕組みがあります。その中でも一番手軽なのが「シンプルトリガー」と呼ばれるもので、onEdit という決まった名前の関数を作っておくだけで、セルを編集するたびに自動で動いてくれます。設定画面を触る必要すらありません。
STEP1:チェックボックスを設置する
コードを書く前に、まずはスプレッドシート側の準備をします。チェックボックスの入れ方から説明しますね。
チェックボックスの挿入手順
チェックボックスは、メニューから簡単に入れられます。
- TODOシートを開き、チェックボックスを置きたいセル範囲を選択します(例:E2からE20あたりまで)。
- 上部メニューの「挿入」をクリックします。
- その中の「チェックボックス」を選びます。
これだけで、選んだセルにチェックボックスが並びます。クリックすると「レ点(チェック)」が入り、もう一度クリックすると外れます。
チェックボックスの中身は TRUE / FALSE
見た目はチェックマークですが、スプレッドシートの内部では、チェックが入っているセルは TRUE、入っていないセルは FALSE という値になっています。これがあとで「チェックが入ったかどうか」を判定するときに効いてきます。今の段階では「チェック=TRUE、未チェック=FALSE なんだな」とだけ覚えておいてください。
シート名と列の位置をメモしておく
コードでは「どのシートの、何列目を見張るか」を指定します。ここがズレると動かないので、今のうちに確認しておきましょう。列は左からA=1、B=2、C=3、D=4、E=5…と数えます。今回はE列にチェックボックスを置いたので、チェック列は「5列目」です。この「5」という数字を、あとでコードに書き込みます。
STEP2:スクリプトエディタを開いてコードを貼る
準備ができたら、いよいよGASのコードを書きます。といっても、下のコードをコピペするだけで動きます。
スクリプトエディタの開き方
スプレッドシートの上部メニューから「拡張機能」→「Apps Script」をクリックします。すると、コードを書くための別画面(スクリプトエディタ)が開きます。最初から function myFunction() {} のような文字が書かれていることがありますが、それはまるごと消して大丈夫です。
最初のコードを貼る
まずは、動きの土台になるコードです。下のコードを、エディタにまるごと貼り付けてください。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// ▼ここで「どのシートの何列目を見張るか」を決めます(あとで自由に変更OK)
const SRC_SHEET = 'TODO'; // チェックを入れる元のシート名
const DONE_SHEET = '完了'; // 移動先のシート名
const CHECK_COL = 5; // チェックボックスがある列(A=1, B=2…なのでE列=5)
// onEdit という名前の関数は、セルを編集するたびに自動で動きます(シンプルトリガー)
function onEdit(e) {
// e には「どのセルをどう編集したか」の情報が入っています
const range = e.range; // 編集されたセル範囲
const sheet = range.getSheet(); // そのセルがあるシート
// ① 関係ない編集はここで打ち切ります(早期return)
if (sheet.getName() !== SRC_SHEET) return; // TODOシート以外なら何もしない
if (range.getColumn() !== CHECK_COL) return; // チェック列以外の編集なら何もしない
// ② チェックが「入った」ときだけ処理を進めます
// e.value は編集後の値。チェックが入ると文字列 'TRUE' になります
if (e.value !== 'TRUE') return; // TRUE以外(外した・空欄など)は無視
// ③ ここまで来たら「TODOシートのチェック列に、チェックが入った」状態
moveRowToDone_(sheet, range.getRow()); // 実際に行を移す処理を呼び出す
}
コードの中の // から後ろは「コメント」といって、人間向けの説明書きです。プログラムの動きには影響しないので、日本語で自分用のメモを書き足しても大丈夫です。
まだ動かない部分がある点に注意
上のコードの最後に moveRowToDone_(...) という行がありますが、この moveRowToDone_ という関数はまだ作っていません。次のSTEPで、実際に行を移動させる中身を書き足していきます。
STEP3:行を移動させる中身を書く
いよいよ本題の「行を別シートへ移す」処理です。やることは3つだけです。(1)対象の行のデータを読み取る、(2)完了シートに追記する、(3)元の行を消す。順番に見ていきましょう。
行を丸ごと読んで、完了シートへ追記する
先ほどのコードの下に、続けて次の関数を貼り付けてください。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// 指定した行を「完了」シートへ移動させる処理
function moveRowToDone_(srcSheet, row) {
const ss = srcSheet.getParent(); // このスプレッドシート全体を取得
const doneSheet = ss.getSheetByName(DONE_SHEET); // 移動先「完了」シートを取得
// 移動先シートが見つからないときは、安全のため何もしないで終わります
if (!doneSheet) {
SpreadsheetApp.getActiveSpreadsheet()
.toast('「' + DONE_SHEET + '」シートが見つかりません', '注意', 5);
return;
}
const lastCol = srcSheet.getLastColumn(); // 元シートの最後の列(=1行の横幅)
// ① 対象の行を丸ごと読み取ります(row行の1列目から、lastCol列ぶん)
// getValues() は二次元配列で返ってくるので [0] で1行ぶんを取り出します
const rowValues = srcSheet.getRange(row, 1, 1, lastCol).getValues()[0];
// ② 完了シートの一番下に、その1行を追記します
doneSheet.appendRow(rowValues);
// ③ 元シートから、その行を削除します
srcSheet.deleteRow(row);
}
これで完成です。TODOシートのチェックボックスにチェックを入れると、その行が「完了」シートの一番下に追記され、TODOシートからは消えます。
コードを保存して権限を許可する
コードを貼ったら、エディタ上部の「保存」ボタン(フロッピーディスクのマーク)を押します。シンプルトリガーの onEdit は、この保存だけで有効になります。
初めて動かすとき、Googleから「このスクリプトにアクセスを許可しますか?」という確認が出ることがあります。自分のスプレッドシートを操作するだけなので、内容を確認したうえで許可して進めてください。あとはスプレッドシートに戻って、チェックボックスにチェックを入れてみましょう。行がスッと移動したら成功です。
appendRow と getValues の役割まとめ
ここで使ったメソッドの役割を、表にまとめておきます。丸暗記する必要はありませんが、「これは何をしているんだっけ?」と迷ったときに戻ってきてください。
| メソッド | 何をするか |
|---|---|
getRange(row, 1, 1, lastCol) | row行の1列目から、lastCol列ぶんの範囲を指定 |
getValues() | 指定範囲の値を二次元配列で読み取る |
appendRow(配列) | シートの一番下の行に、配列の中身を1行として追記 |
deleteRow(row) | row行を丸ごと削除する |
STEP4:チェックの判定を安定させる(isChecked)
ここまでのコードは e.value が 'TRUE' かどうかで判定していました。手軽で分かりやすいのですが、環境によっては e.value がうまく取れないことがあります。そこで、より確実な判定方法も紹介しておきます。
e.value が文字列である点の注意
まず押さえておきたいのが、e.value は「数値のTRUE」ではなく「文字列の ‘TRUE’」だということです。だから if (e.value === true)(真偽値のtrue)と書いても一致せず、うまく動きません。文字列として 'TRUE' と比べる必要があります。ここは初心者がつまずきやすいポイントなので、シングルクォートで囲む点に注意してください。
isChecked() を使うともっと確実
GASには、チェックボックスの状態を直接調べる isChecked() というメソッドがあります。これを使うと、値が文字列かどうかを気にせず「チェックが入っているか」をそのまま判定できます。先ほどの onEdit の②の部分を、次のように書き換えると安定します。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() !== SRC_SHEET) return; // TODOシート以外は無視
if (range.getColumn() !== CHECK_COL) return; // チェック列以外は無視
// isChecked() は、そのセルにチェックが入っていれば true を返します
// 未チェック・チェックボックスでないセルなら false や null になります
if (range.isChecked() !== true) return; // チェックが入っていなければ何もしない
moveRowToDone_(sheet, range.getRow()); // 行を移動
}
isChecked() は、チェックが入っていれば true、入っていなければ false、そもそもチェックボックスでなければ null を返します。なので !== true と書いておけば、「確実にチェックが入ったときだけ」処理を進められます。個人的には、こちらの書き方のほうが安心して使えるのでおすすめです。
見出し行を守る(1行目を無視する)
TODOシートの1行目に「やること」「メモ」といった見出しを置いている場合、うっかり見出し行のチェック列を触っても反応しないようにしておくと安全です。次の1行を足すだけでOKです。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
// onEdit の中、判定の前に足す(1行目=見出し行は処理しない)
if (range.getRow() === 1) return; // 見出し行のチェックは無視する
つまずきポイントと回避策
ここからは、私が実際に作ったときにハマったポイントと、その回避策をまとめます。同じところで悩まないよう、先に共有しておきますね。
行を消すと番号がずれる(複数同時チェックの罠)
一番の落とし穴がこれです。deleteRow(row) で行を消すと、その下の行が1つずつ繰り上がります。つまり行番号がズレるんです。
チェックを1個ずつ入れる分には問題ありません(消えたらまた最新の行番号でチェックが動くため)。でも、チェックボックス列をまとめてコピペしたり、複数セルを一気に貼り付けたりすると、onEdit が複数行ぶんまとめて動くことがあり、行番号のズレで意図しない行が消えることがあります。
回避策:複数行は「下から」処理する
まとめてチェックが入るケースにも対応したいなら、編集された範囲を調べて、下の行から順番に処理します。行を下から消していけば、上の行の番号はズレないので安全です。
// ※構文・API仕様を確認済み(GAS V8ランタイム向け)
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() !== SRC_SHEET) return; // TODOシート以外は無視
if (range.getColumn() !== CHECK_COL) return; // チェック列以外は無視
const startRow = range.getRow(); // 編集範囲の先頭行
const numRows = range.getNumRows(); // 編集された行数(1個なら1)
// 下の行から上に向かってループします(i を大きい方から小さい方へ)
// こうすると行を消しても、まだ処理していない上の行の番号がズレません
for (let i = numRows - 1; i >= 0; i--) {
const row = startRow + i; // 今チェックする行番号
if (row === 1) continue; // 見出し行はスキップ
const cell = sheet.getRange(row, CHECK_COL);
if (cell.isChecked() === true) { // その行にチェックが入っていれば
moveRowToDone_(sheet, row); // 完了シートへ移動
}
}
}
普段は1個ずつチェックするだけなら、STEP3までのシンプルなコードで十分です。「まとめて処理することがあるかも」という方は、この下から処理する版を使ってください。
反応が遅い・動かないときの確認
「チェックしたのに動かない」ときは、次の3点を順番に確認すると、たいてい原因が見つかります。
| 確認すること | 見るポイント |
|---|---|
| シート名は合っているか | SRC_SHEET の文字とシートのタブ名が完全一致か(全角空白などに注意) |
| 列番号は合っているか | CHECK_COL の数字が、実際のチェックボックスの列と同じか |
| 保存したか | コードを直したあと「保存」ボタンを押したか |
シート名は、見た目が同じでも余分なスペースが入っていると一致しません。コピペで合わせるのが確実です。
シンプルトリガーの制約を正直に
最後に、大事な注意点をお伝えします。今回使った onEdit は「シンプルトリガー」という手軽な仕組みですが、手軽なぶん、できないこともあります。ここは正直にお伝えしておきますね。
メール送信などはできない
シンプルトリガーの onEdit からは、Gmailを送るなど「本人の許可(認証)が必要なサービス」を呼び出せません。たとえば「チェックが入ったら自分にメールで知らせる」といったことは、今回の onEdit の中には書けないんです。ここは仕様上の制約なので、無理に書いてもエラーになります。
実行時間や共有まわりの制約
シンプルトリガーは実行できる時間が短め(おおよそ30秒程度)に制限されています。何百行もまとめて動かすような重い処理には向きません。また、スプレッドシートを他の人と共有している場合、他の人が編集しても onEdit は動きます(動かすのはあくまで「編集した瞬間」なので、誰が編集しても反応します)。家族と共有しているリストなら便利ですが、意図しない人の編集で動いてほしくないときは注意してください。
もっと複雑にしたいなら「インストーラブルトリガー」
「チェックが入ったらメールで通知したい」など、認証が必要なことをやりたくなったら、「インストーラブルトリガー」という別の仕組みに切り替えます。これは、関数名は同じ onEdit のままでも、スクリプトエディタの設定画面から手動で登録するタイプのトリガーで、Gmail送信などの権限を使えます。設定の仕方は少し手順が増えるので、別の記事で詳しく紹介します(末尾の関連記事「トリガー設定」を参照してください)。まずは今回のシンプルトリガーで「自動で行が動く楽しさ」を味わってもらえたら嬉しいです。
移動する前に一呼吸おきたいとき
チェックを入れた瞬間に消えるのがちょっと怖い、という方もいると思います(私も最初そうでした)。そんなときは、いきなり削除せず、まず確認用の列に日時だけ記録しておいて、あとでまとめて移動する、という運用にすると安心です。あるいは、完了シートに移す前に「本当に移動しますか?」的な確認を挟む作りにもできます。慣れるまでは、完了シートに移すだけにして deleteRow の行をコメントアウト(行頭に // を付けて無効化)しておき、元シートには残す、という使い方から始めるのもおすすめです。
自分でも作れるようになりたい方へ
ここまで読んで、「思ったより簡単そう」「自分の管理表にも入れてみたい」と感じてもらえたら嬉しいです。今回の仕組みは、コードそのものは短いのに、毎日の切り取り&貼り付け作業をまるごと消してくれる、コスパの高い一品です。
私も最初はコードなんて全然書けませんでしたが、こうして「小さく作って、動いた!」を積み重ねているうちに、少しずつ自分の生活に合わせて改造できるようになりました。看護師でもコードは書けます。あなたにもきっとできます。もし体系的に学んでみたくなったら、無料で試せる学習サービスから覗いてみるのもおすすめです。
Dive into Code(未経験からエンジニアを目指すプログラミングスクール)
関連記事(あわせて読みたい)
- /blog/gas-sheet-timestamp-auto/ … 入力した瞬間に日時を自動で記録する方法。今回の「移動前に日時をメモしておく」運用と相性抜群です。
- /blog/gas-trigger-setup/ … メール通知など一歩進んだ自動化に必要な「インストーラブルトリガー」の設定手順をやさしく解説。
- /blog/gas-sheet-filter-auto/ … 条件に合う行だけを自動で絞り込む方法。完了シートを見やすく整理するのに役立ちます。
この記事を書いた人:凛
2児のママで現役ナース。夜勤明けの細切れ時間を副業GASに投じ、月5〜8万円の副収入を継続中。「看護師でもコードは書ける」を合言葉に、家事育児とプログラミングを両立する等身大の情報を発信しています。
掲載コードは構文とAPI仕様を確認して載せていますが、お使いの環境に合わせて調整してください。