VBA → GAS 対応表
Excel VBA でよく使う書き方 15 種類と、Google Apps Script(GAS)での書き換え例です。このページの GAS のコードはすべてテスト環境で実行し、VBA と同じ結果になることを確認しています。
ブック全体を移行するなら:マクロを無料で診断する特定の列の最終行
End(xlUp) は 1 つの列だけを見ます。sheet.getLastRow() はすべての列を見るため、列ごとに長さが違うと結果が変わります。
Sub ShowLastRow()
Dim last As Long
last = Cells(Rows.Count, 1).End(xlUp).Row
Range("C1").Value = last
End Subfunction ShowLastRow() {
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange('C1').setValue(lastRowInColumn_(sheet, 1));
}
// Last non-empty row in one column (1 if the column is empty).
function lastRowInColumn_(sheet, col) {
const values = sheet.getRange(1, col, Math.max(sheet.getLastRow(), 1), 1).getValues();
for (let r = values.length - 1; r >= 0; r--) if (values[r][0] !== '') return r + 1;
return 1;
}✓ 確認済み: C1 = 2
行のループを速くする(まとめて読み書き)
getValue / setValue は 1 回ごとに通信が発生し、数十ミリ秒かかります。範囲をまとめて配列に読み込み、処理してから 1 回で書き戻します。
Sub AddTax()
Dim i As Long
For i = 2 To 4
Cells(i, 2).Value = Int(Cells(i, 1).Value * 1.1)
Next i
End Subfunction AddTax() {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange(2, 1, 3, 2);
const rows = range.getValues().map(([price]) => [price, Math.floor(price * 1.1)]);
range.setValues(rows);
}✓ 確認済み: B2 = 110 · B3 = 275 · B4 = 1098
MsgBox
メッセージボックスは SpreadsheetApp.getUi() のダイアログになります。
Sub Hello()
MsgBox "Done"
End Subfunction Hello() {
SpreadsheetApp.getUi().alert('Done');
}✓ 確認済み: Done
はい / いいえ の確認
vbYes の代わりに ui.Button.YES と比べます。
Sub ConfirmClear()
If MsgBox("Clear the sheet?", vbYesNo) = vbYes Then
Range("A1").Value = ""
End If
End Subfunction ConfirmClear() {
const ui = SpreadsheetApp.getUi();
if (ui.alert('Confirm', 'Clear the sheet?', ui.ButtonSet.YES_NO) === ui.Button.YES) {
SpreadsheetApp.getActiveSheet().getRange('A1').setValue('');
}
}✓ 確認済み: A1 = "keep me"
InputBox(キャンセル対応)
InputBox はキャンセル時に空文字を返します。ui.prompt では押されたボタンで判定します。
Sub AskName()
Dim s As String
s = InputBox("Your name?")
If s = "" Then Exit Sub
Range("A1").Value = s
End Subfunction AskName() {
const ui = SpreadsheetApp.getUi();
const res = ui.prompt('Your name?');
if (res.getSelectedButton() !== ui.Button.OK || res.getResponseText() === '') return;
SpreadsheetApp.getActiveSheet().getRange('A1').setValue(res.getResponseText());
}✓ 確認済み: A1 = "Aiko"
Worksheets("名前") とシートがない場合
getSheetByName はエラーにならず null を返すため、VBA なら止まっていた箇所で確認が必要です。
Sub CopyTotal()
Worksheets("Report").Range("A1").Value = Worksheets("Data").Range("B2").Value
End Subfunction CopyTotal() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const data = ss.getSheetByName('Data');
const report = ss.getSheetByName('Report');
if (!data || !report) throw new Error('Sheet "Data" or "Report" not found');
report.getRange('A1').setValue(data.getRange('B2').getValue());
}✓ 確認済み: A1 = 42
Round(銀行型の丸め)
VBA の Round(2.5) は 2、Round(3.5) は 4 です。Math.round は .5 を常に切り上げるため、気づかないうちに結果が変わります。
Sub RoundDemo()
Range("A1").Value = Round(2.5)
Range("A2").Value = Round(3.5)
Range("A3").Value = Round(2.345, 2)
End Subfunction RoundDemo() {
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange('A1:A3').setValues([[vbaRound_(2.5)], [vbaRound_(3.5)], [vbaRound_(2.345, 2)]]);
}
// VBA Round: halves go to the nearest even digit.
function vbaRound_(value, digits = 0) {
const f = Math.pow(10, digits);
const x = Number((value * f).toPrecision(15));
const r = Math.round(x);
return (Math.abs(x % 1) === 0.5 && r % 2 !== 0 ? r - 1 : r) / f;
}✓ 確認済み: A1 = 2 · A2 = 4 · A3 = 2.34
Int・Fix・Mod(負の数と小数)
Int は切り下げ、Fix は 0 方向への切り捨て、Mod は計算前に両方を整数に丸めます(12.6 Mod 5 = 3)。
Sub MathDemo()
Range("A1").Value = Int(-2.5)
Range("A2").Value = Fix(-2.5)
Range("A3").Value = 12.6 Mod 5
End Subfunction MathDemo() {
const sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange('A1:A3').setValues([[Math.floor(-2.5)], [Math.trunc(-2.5)], [vbaMod_(12.6, 5)]]);
}
function vbaMod_(a, b) {
const toInt = (x) => (Math.abs(x % 1) === 0.5 ? 2 * Math.round(x / 2) : Math.round(x));
return toInt(a) % toInt(b);
}✓ 確認済み: A1 = -3 · A2 = -2 · A3 = 3
Left・Mid・Right・InStr
VBA の位置は 1 始まりで、InStr は見つからないと 0 を返します。JavaScript は 0 始まりで、indexOf は -1 を返します。
Sub StringDemo()
Dim s As String: s = "INV-2026-001"
Range("A1").Value = Left(s, 3)
Range("A2").Value = Mid(s, 5, 4)
Range("A3").Value = Right(s, 3)
Range("A4").Value = InStr(s, "2026")
Range("A5").Value = InStr(s, "X")
End Subfunction StringDemo() {
const s = 'INV-2026-001';
const out = [
s.slice(0, 3),
s.substr(5 - 1, 4), // Mid(s, 5, 4): start is 1-based
s.slice(-3),
s.indexOf('2026') + 1, // InStr: 1-based, 0 when missing
s.indexOf('X') + 1,
];
SpreadsheetApp.getActiveSheet().getRange(1, 1, out.length, 1).setValues(out.map((v) => [v]));
}✓ 確認済み: A1 = "INV" · A2 = "2026" · A3 = "001" · A4 = 5 · A5 = 0
日付の書式(Format)
Utilities.formatDate をスプレッドシートのタイムゾーンで使います。月は MM(mm は分)です。
Sub DateLabel()
Range("A1").Value = "Issued " & Format(DateSerial(2026, 4, 1), "yyyy/mm/dd")
End Subfunction DateLabel() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const d = new Date(2026, 3, 1); // months are 0-based in JavaScript
const text = Utilities.formatDate(d, ss.getSpreadsheetTimeZone(), 'yyyy/MM/dd');
ss.getActiveSheet().getRange('A1').setValue('Issued ' + text);
}✓ 確認済み: A1 = "Issued 2026/04/01"
月末日と月の加算
DateSerial(y, m + 1, 0) は new Date(y, m, 0) に相当します。DateAdd("m") は 1/31 の 1 か月後を 2/28 にしますが、setMonth だけだと 3/3 になります。
Sub Dates()
Range("A1").Value = Format(DateSerial(2026, 3, 0), "yyyy/mm/dd")
Range("A2").Value = Format(DateAdd("m", 1, DateSerial(2026, 1, 31)), "yyyy/mm/dd")
End Subfunction Dates() {
const tz = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone();
const fmt = (d) => Utilities.formatDate(d, tz, 'yyyy/MM/dd');
SpreadsheetApp.getActiveSheet().getRange('A1:A2').setValues([
[fmt(new Date(2026, 2, 0))],
[fmt(addMonths_(new Date(2026, 0, 31), 1))],
]);
}
// Like VBA DateAdd("m"): keeps the day, clamped to the target month's length.
function addMonths_(date, n) {
const d = new Date(date.getFullYear(), date.getMonth() + n, 1);
const last = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate();
d.setDate(Math.min(date.getDate(), last));
return d;
}✓ 確認済み: A1 = "2026/02/28" · A2 = "2026/02/28"
Scripting.Dictionary → Map
Exists → has、Item → get / set、Count → size、Keys → [...map.keys()]。
Sub TotalsByCustomer()
Dim d As Object, i As Long
Set d = CreateObject("Scripting.Dictionary")
For i = 2 To 5
d(Cells(i, 1).Value) = d(Cells(i, 1).Value) + Cells(i, 2).Value
Next i
Range("D1").Value = d("A")
Range("D2").Value = d.Count
End Subfunction TotalsByCustomer() {
const sheet = SpreadsheetApp.getActiveSheet();
const totals = new Map();
for (const [name, amount] of sheet.getRange(2, 1, 4, 2).getValues()) {
totals.set(name, (totals.get(name) || 0) + amount);
}
sheet.getRange('D1:D2').setValues([[totals.get('A')], [totals.size]]);
}✓ 確認済み: D1 = 17 · D2 = 3
On Error GoTo → try/catch(0 での割り算)
JavaScript は 0 で割ってもエラーにならず Infinity を返します。VBA がエラーに頼っていた箇所は、割る数を自分で確認します。
Sub UnitPrice()
On Error GoTo Failed
Range("C1").Value = Range("A1").Value / Range("B1").Value
Exit Sub
Failed:
Range("C1").Value = "error"
End Subfunction UnitPrice() {
const sheet = SpreadsheetApp.getActiveSheet();
try {
const [[total, qty]] = sheet.getRange('A1:B1').getValues();
if (qty === 0 || qty === '') throw new Error('Division by zero');
sheet.getRange('C1').setValue(total / qty);
} catch (e) {
sheet.getRange('C1').setValue('error');
}
}✓ 確認済み: C1 = "error"
ループで行を削除する
下から上へ削除するか、filter で残す行だけを作って 1 回で書き戻します(こちらが速い)。
Sub RemoveDone()
Dim i As Long
For i = 5 To 2 Step -1
If Cells(i, 2).Value = "done" Then Rows(i).Delete
Next i
End Subfunction RemoveDone() {
const sheet = SpreadsheetApp.getActiveSheet();
const range = sheet.getRange(2, 1, 4, 2);
const keep = range.getValues().filter(([, status]) => status !== 'done');
range.clearContent();
if (keep.length) sheet.getRange(2, 1, keep.length, 2).setValues(keep);
}✓ 確認済み: A2 = "b" · A3 = "d" · A4 = ""
Debug.Print
console.log を使います。出力は Apps Script の「実行数」ログに表示されます。
Sub Trace()
Debug.Print "rows: " & 3
End Subfunction Trace() {
console.log('rows: ' + 3);
}✓ 確認済み: rows: 3
コード例は MIT ライセンスです。自由にコピーしてお使いください。