1.何を自動化するか — 判断の基準
自動化とは、人が手で行っていた繰り返しの作業をコードに肩代わりさせることです。大がかりなプログラムである必要はありません。毎週届く販売記録から品目ごとの合計を出したり、あちこちから集めた名簿の空白や重複を整理したりする数十行のコードも、立派な自動化です。
ただし、すべての仕事をコードにする必要はありません。一度きりで10分で終わる仕事なら、コードを書いて確かめる時間のほうが長くかかることもあります。逆に、1回10分かかる仕事を毎週しているなら、1年で520分、約9時間になります。同じ規則で繰り返され、量が多く、手作業だとミスが起きやすい仕事ほど、自動化する価値が大きくなります。
規則がはっきりしているかどうかも大切です。「電話番号から数字だけを残して090-0000-0000の形に直す」という規則はコードに書きやすいものです。一方、「ぎこちない文章を自然に直す」のように判断が必要な仕事は、コードよりも人やAIアシスタントによる見直しが向いています。規則を言葉ではっきり書き出せればコードにも書ける、という第1講の考え方がここでもそのまま当てはまります。
| 基準 | 自分に問いかけること | 自動化に傾くサイン |
|---|---|---|
| 繰り返し | どのくらいの頻度で行うか | 毎日・毎週・毎月繰り返している |
| 規則 | 処理の規則を言葉で書き出せるか | 例外まで含めて数行にまとまる |
| 量 | 一度に何件扱うか | 数十件以上あり、目で追うのがつらい |
| ミスの代償 | 間違えたらどれほど困るか | 手作業だと抜け漏れや打ち間違いがよく起きる |
| 変化 | 入力の形がよく変わるか | 列の順序や形式がほとんど変わらない |
2.カンマ区切りのテキストを表にする
表計算ソフトから書き出したデータやチャットで受け取った一覧は、多くの場合「1行に1件、項目はカンマ区切り」という形をしています。この形式をCSV(カンマで区切った値)と呼びます。1行目はふつう列の名前(見出し)で、2行目からが実際の記録です。
コードにする手順は単純です。まず第7講で学んだ split("\n") で行に分け、各行をさらに split(",") で項目に分けます。次に第6講のオブジェクトで項目ごとに名前を付ければ「オブジェクトの配列」、つまりコードの中の表になります。前後の空行のせいで空の記録ができないよう、先に trim() しておくとよいでしょう。
大事な落とし穴が一つあります。split で分けた項目はすべて文字列です。数字に見えても "10" は文字列なので、そのまま足すと第2講で見たようにつなげられてしまいます。数量や金額の列は Number() で数値に変えておかないと、計算が合いません。
slice(1) で飛ばします。const text = `日付,品目,数量,単価
2026-03-01,鉛筆,10,500
2026-03-01,ノート,3,2000
2026-03-02,鉛筆,5,500
2026-03-02,消しゴム,4,300`;
const lines = text.trim().split("\n");
console.log(lines[0].split(","));
const rows = lines.slice(1).map((line) => {
const c = line.split(",");
return {
date: c[0],
item: c[1],
qty: Number(c[2]),
price: Number(c[3]),
};
});
console.log(rows.length);
console.log(rows[0]);[ '日付', '品目', '数量', '単価' ]
4
{ date: '2026-03-01', item: '鉛筆', qty: 10, price: 500 }+ は連結です。数字以外の文字が混じると、Number は NaN を返します。const c = "2026-03-01,鉛筆,10,500".split(",");
console.log(c[2] + c[3]);
console.log(Number(c[2]) * Number(c[3]));
console.log(Number("10個"));10500 5000 NaN
3.合計・平均・グループ別合計
表ができたら、第4講の累積パターンで合計を出します。変数を一つ0で用意し、記録を一つずつ回しながら足していけばよいのです。平均は合計を記録の件数で割った値ですが、記録が0件だと0で割ることになり NaN が出るので、先に確かめます。小数が長く続くときは Math.round(x * 10) / 10 のように、丸める桁を決めておきます。
「品目別の合計」「部署別の経費」のように、グループごとに分けて足すことをグループ別合計といいます。空のオブジェクトを一つ作り、記録ごとにそのグループ名をキーにして値を足します。初めて出てきたキーの値は undefined なので、(合計[キー] || 0) + 値 のように0から始めるのがコツです。表計算ソフトのピボットテーブルがしていることを、数行で自分でやってみるわけです。
計算が終わったら、数字を一つ必ず手で検算します。下の例では、鉛筆は10個×500ウォンと5個×500ウォンなので7,500ウォンになるはずです。一つのグループだけでも自分で照らし合わせてみれば、列を取り違えたり、文字列を数値に変え忘れたりしたミスにすぐ気づけます。
const rows = [
{ item: "鉛筆", qty: 10, price: 500 },
{ item: "ノート", qty: 3, price: 2000 },
{ item: "鉛筆", qty: 5, price: 500 },
{ item: "消しゴム", qty: 4, price: 300 },
];
let total = 0;
let qtySum = 0;
const byItem = {};
for (const r of rows) {
const amount = r.qty * r.price;
total += amount;
qtySum += r.qty;
byItem[r.item] = (byItem[r.item] || 0) + amount;
}
console.log("総売上", total);
console.log("平均数量", qtySum / rows.length);
console.log(byItem);総売上 14700
平均数量 5.5
{ '鉛筆': 7500, 'ノート': 6000, '消しゴム': 1200 }const text = `部署,金額
営業,32000
開発,15000
総務,9000
営業,18000
開発,26000`;
const sums = {};
for (const line of text.split("\n").slice(1)) {
const [team, money] = line.split(",");
sums[team] = (sums[team] || 0) + Number(money);
}
console.log(sums);
let best = null;
for (const [team, sum] of Object.entries(sums)) {
if (best === null || sum > best[1]) {
best = [team, sum];
}
}
console.log("最も多く使った部署", best[0]);{ '営業': 50000, '開発': 41000, '総務': 9000 }
最も多く使った部署 営業- ステップ1 テキストを行に分け、見出しを飛ばします。
- ステップ2 各行をカンマで分けて部署と金額を取り出し、金額は
Numberで数値に変えます。コードのconst [team, money] = …は、分けた配列の1番目と2番目の値を順に二つの名前に入れる書き方(分割代入)です。 - ステップ3 空のオブジェクトに、部署名をキーとして金額を足していきます。
- ステップ4
Object.entriesでオブジェクトを[部署, 合計]の組の配列に変え、繰り返しながら最大の値を覚えておきます。 - 検算:営業は32,000 + 18,000 = 50,000ウォンで、コードの結果と同じか確かめます。
4.テキストの整理 — 空白・空行・重複
何人もが手で入力した名簿には、前後の空白、二つ続いた空白、空行、同じ人が2回入った行などがよく混じっています。目には同じに見えても、コンピューターにとって "健太" と "健太 " は別の文字列なので、整理しないと重複の削除や検索がうまくいきません。
整理の規則はたいてい3段階です。trim() で前後の空白を消し、replace(/\s+/g, " ") で連続した空白を一つにまとめ、filter で空文字列を取り除きます。\s+ は「空白文字が一つ以上」という意味の正規表現で、第7講で少し触れたものです。正規表現がわかりにくいときは、正規表現テスターに例文を入れて、何に一致するかを先に見てみましょう。
重複の削除には Set が便利です。Set は同じ値を一度しか入れない入れ物なので、[...new Set(配列)] で配列に戻すと、最初に出てきた順のまま重複が取り除かれます。メールアドレスのように、ふつう大文字と小文字を区別せずに扱う値は、先に toLowerCase() でそろえてから重複を取り除く必要があります。整理の前後の件数も一緒に出しておけば、何件減ったかがすぐにわかります。
const raw = [
" 健太 ",
"山田 花子",
"健太",
"",
"翔 ",
"山田 花子",
];
const cleaned = raw
.map((s) => s.trim().replace(/\s+/g, " "))
.filter((s) => s !== "");
console.log(cleaned);
const unique = [...new Set(cleaned)];
console.log(unique);
console.log(raw.length, "→", unique.length);[ '健太', '山田 花子', '健太', '翔', '山田 花子' ] [ '健太', '山田 花子', '翔' ] 6 → 3
const emails = ["[email protected]", "[email protected] "];
const norm = emails.map((e) => e.trim().toLowerCase());
console.log([...new Set(norm)]);[ '[email protected]' ]
5.電話番号・日付の形式をそろえる
同じ電話番号でも、090-1234-5678、09012345678、090 1234 5678 のようにさまざまな形で入ってきます。形式をそろえる最も確実な方法は「数字だけを残してから組み立て直す」ことです。replace(/\D/g, "") は数字以外の文字をすべて消します。残った数字が11桁で 0 から始まっていれば、携帯電話番号として3-4-4で区切ってハイフンを入れます。
規則に合わない値は無理に直さず、null のように「処理できない」という印を返すのがよい方法です。そうしておけば、あとでその行だけを集めて人が確認できます。自動化が知らない値をもっともらしく書き換えてしまうと、間違ったデータが静かに紛れ込み、手作業のミスよりも見つけにくくなります。
日付も 2026.3.5.、2026/03/05、 2026-3-15 のようにまちまちです。空白と末尾の点を消し、.・/・- のどれででも分けたうえで、月と日を padStart(2, "0") で2桁にそろえ、2026-03-05 の形にします。この形は文字列のまま並べ替えても日付順になるので便利です。下の関数は月が1〜12、日が1〜31かどうかしか確かめないので、2月30日のような日付ははじけません。こうした限界はコメントに残しておきます。
null で印を付けて、別に確認します。function formatPhone(s) {
const d = s.replace(/\D/g, "");
if (d.length !== 11 || !d.startsWith("0")) {
return null;
}
return d.slice(0, 3) + "-" + d.slice(3, 7) +
"-" + d.slice(7);
}
const inputs = [
"090-1234-5678",
"09012345678",
"090 1234 5678",
"(090)1234.5678",
"1234-5678",
];
for (const p of inputs) {
console.log(p, "→", formatPhone(p));
}090-1234-5678 → 090-1234-5678 09012345678 → 090-1234-5678 090 1234 5678 → 090-1234-5678 (090)1234.5678 → 090-1234-5678 1234-5678 → null
3/5/2026 のように順序が違う形式は、年の位置が合わないので null になります。// 月1〜12、日1〜31だけを確かめる(2月30日ははじけない)
function formatDate(s) {
const t = s.replace(/\s/g, "").replace(/\.$/, "");
const parts = t.split(/[.\/-]/).map(Number);
if (parts.length !== 3) return null;
const y = parts[0], m = parts[1], d = parts[2];
if (!Number.isInteger(y) || y < 1000) return null;
if (!(m >= 1 && m <= 12 && d >= 1 && d <= 31)) {
return null;
}
const mm = String(m).padStart(2, "0");
const dd = String(d).padStart(2, "0");
return y + "-" + mm + "-" + dd;
}
const dates = [
"2026.3.5.",
"2026/03/05",
" 2026-3-15 ",
"2026. 12. 1",
"2026.13.1",
"3/5/2026",
];
for (const x of dates) {
console.log(JSON.stringify(x), "→", formatDate(x));
}"2026.3.5." → 2026-03-05 "2026/03/05" → 2026-03-05 " 2026-3-15 " → 2026-03-15 "2026. 12. 1" → 2026-12-01 "2026.13.1" → null "3/5/2026" → null
6.元データを守り、結果を確かめる
自動化でいちばん大きな事故は、元データを上書きして元に戻せなくなることです。コードは元データを読むだけにし、結果は新しい変数や新しいファイルに書くことを原則にします。ファイルを扱うなら作業の前に元データを別にコピーしておき、結果のファイル名には日付や「整理済み」を付けて区別します。第6講で見たように、map・filter は新しい配列を作り、元の配列はそのまま残すので、この原則によく合います。
結果は三つの方法で確かめます。一つ目は件数の確認です。元の記録数、結果の記録数、はじいた数を出して、足し算が合うかを見ます。二つ目は抜き取り確認です。結果から何行かを選んで元データと手で見比べ、特に先頭・末尾・おかしく見える行を見ます。三つ目は合計の確認です。表計算ソフトで出した合計とコードの合計が同じかを照らし合わせます。整理の前後のテキストをテキスト比較ツールに入れれば、変わったところだけがひと目でわかります。
実際のCSVファイルでは、この講義の単純な split(",") がうまくいかない場合があります。項目の中にカンマが入るときはその項目をダブルクォートで囲む決まりがあるため、"鉛筆, 青" のように書かれた一つの項目が二つに切れてしまいます。項目の中の改行や、引用符の中の引用符といった決まりもあります。そのため本物のファイルを扱うときは、表計算ソフトで開いて処理するか、CSVの規則を正しく実装した実績のあるライブラリ(パーサー)を使います。この講義のやり方は、形式を自分で決めた単純なテキストに向いています。
split は引用符の決まりを知りません。const line = '2026-03-03,"鉛筆, 青",2,500';
const cells = line.split(",");
console.log(cells.length);
console.log(cells);5 [ '2026-03-03', '"鉛筆', ' 青"', '2', '500' ]
\r\n です。おかしな行は捨てずに「保留」として集め、人が確認します。const text = "名前,金額\r\n健太,3000\r\n\r\n花子,abc\r\n";
const lines = text.split(/\r?\n/).slice(1);
const ok = [];
const bad = [];
for (const line of lines) {
if (line.trim() === "") continue;
const c = line.split(",");
const n = Number(c[1]);
if (c.length === 2 && Number.isFinite(n)) {
ok.push({ name: c[0], amount: n });
} else {
bad.push(line);
}
}
console.log("処理", ok.length, "保留", bad.length);
console.log(bad);処理 1 保留 1 [ '花子,abc' ]
- 元データは読むだけにし、結果は新しい変数・新しいファイルに書く
- 元の件数 = 処理した件数 + 保留の件数 になっているか確かめる
- 先頭・末尾・おかしな行を選んで、手で見比べる
- 合計の一つは、別の方法(電卓・表計算ソフト)で検算する
- 本物のCSVファイルは、表計算ソフトか実績のあるCSVパーサーで扱う
7.AIアシスタントと一緒に自動化を作る
自動化のコードは、AIコーディングアシスタントに任せやすい仕事です。ただし、よい結果を得るには、入力の形、ほしい出力、例外の扱い方をはっきり伝える必要があります。「CSVを整理して」よりも「見出しが部署,金額のテキストから部署別の合計をオブジェクトで返す関数。金額が数値でない行は別に集めて」のほうが、ずっと正確なコードが返ってきます。
AIにデータを見せるときは、実際の顧客名簿や連絡先を貼り付けず、同じ形の偽のデータを数行だけ見せます。形式さえわかればコードは作れるからです。受け取ったコードは自分のコンピューターで本物のデータに対して動かし、上で述べた件数・抜き取り・合計の確認を同じように行います。
受け取ったコードが正しいかを読んでテストする方法は、次の第10講で詳しく扱います。この講義の実習問題を自分で解いた経験があれば、AIが書いたコードで Number への変換が抜けていたり、空行を処理していなかったりする箇所がずっと見つけやすくなります。
📌 要点まとめ
- 頻繁に繰り返し、規則がはっきりしていて、量が多く、ミスが起きやすい仕事が自動化の候補
- 行は
split("\n")、項目はsplit(",")で分け、数値の列はNumberで変える - グループ別合計は、空のオブジェクトに
(合計[キー] || 0) + 値で積み上げる - 整理の規則に合わない値は無理に直さず、
nullや保留として印を付ける - 元データは守り、件数・抜き取り・合計で結果を確かめ、本物のCSVファイルは専用のパーサーか表計算ソフトで扱う
🧪 コード実習
架空の記録を使って、小さな自動化の関数を作ってみます。空のデータや形式の間違った値のような境界のケースもテストに入っています。
コードはこのブラウザの中の隔離された場所でだけ実行され、サーバーには送られません。ネットワークには接続できず、2秒を超えると止まります。書き換えたコードはこのブラウザにだけ保存されます。Ctrl+Enter(⌘+Enter)で実行、Tabキーは空白2つ(Escの後のTabで次へ移動)。
JavaScriptが無効なので実行はできませんが、問題・最初のコード・自動チェックの内容・解答例はそのまま読めます。
1金額の合計を出す
関数 totalAmount(text) を完成させてください。text は、1行目が見出し 名前,金額 で、2行目から 名前,金額 の記録が続く文字列です。すべての金額の合計を数値で返してください。記録がなければ0、空行は飛ばします。
totalAmount("名前,金額\n健太,3000\n花子,4500")期待値7500totalAmount("名前,金額\n翔,1200\n\n健太,800\n")期待値2000totalAmount("名前,金額")期待値0totalAmount("名前,金額\n花子,0\n翔,250")期待値250
💡 ヒント
c[1] は文字列です。Number(c[1]) で数値に変えて足し、line.trim() === "" なら continue で次へ進みましょう。
解答例を見る
function totalAmount(text) {
const lines = text.split("\n").slice(1);
let total = 0;
for (const line of lines) {
if (line.trim() === "") continue;
const c = line.split(",");
total += Number(c[1]);
}
return total;
}2名簿を整理する
関数 cleanNames(list) を完成させてください。文字列の配列を受け取り、各名前の前後の空白を消して連続した空白を一つにまとめたあと、空の名前を除き、重複をなくした配列を返してください。順序は最初に出てきた順を保ちます。
cleanNames([" 健太 ","花子","健太"])期待値["健太","花子"]cleanNames(["佐藤 翔","佐藤 翔 ",""])期待値["佐藤 翔"]cleanNames([])期待値[]cleanNames([" ","","ひとつ"])期待値["ひとつ"]cleanNames(["花子","健太","花子","健太"])期待値["花子","健太"]
💡 ヒント
replace(/\s+/g, " ") で空白をまとめ、filter で空文字列を除いてから、[...new Set(配列)] で重複をなくします。
解答例を見る
function cleanNames(list) {
const cleaned = list
.map((s) => s.trim().replace(/\s+/g, " "))
.filter((s) => s !== "");
return [...new Set(cleaned)];
}3電話番号の形式をそろえる
関数 formatPhone(s) を完成させてください。文字列から数字だけを残したときに11桁で 0 から始まっていれば 090-1234-5678 の形(3-4-4)で返し、そうでなければ null を返してください。
formatPhone("09012345678")期待値"090-1234-5678"formatPhone("080 9876 5432")期待値"080-9876-5432"formatPhone("(090)1111.2222")期待値"090-1111-2222"formatPhone("1234-5678")期待値nullformatPhone("")期待値nullformatPhone("03-1234-5678")期待値null
💡 ヒント
s.replace(/\D/g, "") で数字だけが残ります。d.length !== 11 || !d.startsWith("0") なら null です。
解答例を見る
function formatPhone(s) {
const d = s.replace(/\D/g, "");
if (d.length !== 11 || !d.startsWith("0")) {
return null;
}
return d.slice(0, 3) + "-" + d.slice(3, 7) +
"-" + d.slice(7);
}4グループ別の合計
関数 sumByGroup(text) を完成させてください。text は、1行目が見出し 部署,金額 の複数行の文字列です。部署名をキー、その部署の金額の合計を値とするオブジェクトを返してください。記録がなければ空のオブジェクト {}、空行は飛ばします。
sumByGroup("部署,金額\n営業,100\n開発,200\n営業,50")期待値{"営業":150,"開発":200}sumByGroup("部署,金額")期待値{}sumByGroup("部署,金額\n総務,0\n\n総務,30\n")期待値{"総務":30}sumByGroup("部署,金額\n開発,10\n開発,20\n開発,30")期待値{"開発":60}
💡 ヒント
今のコードは、同じ部署がもう一度出てくると前の値を上書きしてしまいます。(sums[c[0]] || 0) + Number(c[1]) で足していきましょう。
解答例を見る
function sumByGroup(text) {
const sums = {};
const lines = text.split("\n").slice(1);
for (const line of lines) {
if (line.trim() === "") continue;
const c = line.split(",");
sums[c[0]] = (sums[c[0]] || 0) + Number(c[1]);
}
return sums;
}🤖 AIにはこう聞いてみましょう
コピーして [ ] の部分を自分の状況に書き換えて使います。答えはそのまま信じず、本文と比べて確かめましょう。
繰り返しの作業をコードにしたいとき
私は毎週[作業の説明]を手作業でしています。入力は下の偽の例と同じ形で、出力は[ほしい結果の形]にしたいです。JavaScriptの関数で書いてください。数値に変える列、空行、形式が間違っている行をどう扱うかをコメントで明記してください。形式が間違っている行は捨てずに、別に集めてください。 [偽の例3〜5行]
作った自動化コードを点検してほしいとき
下はCSVのようなテキストを整理する私のJavaScriptコードです。元データを書き換えている部分がないか、文字列を数値に変え忘れている箇所がないか、項目の中のカンマ・空行・行末の `\r` のような場合にどうなるかを指摘し、確認用のテスト入力を5つ作ってください。 [自分のコード]
整理の規則を決めたいとき
[電話番号/日付/名前]の列が、人によって違う形で入力されています。下の偽の例を見て一つにそろえる規則を提案し、規則に合わず人が確認すべきケースを一覧にまとめてください。 [偽の例]
🧰 関連ツール
この講義で扱ったJSON・正規表現・テキストを、その場で確かめたり整えたりできるツールです。本物の個人情報やパスワードは貼り付けないでください。
- MDN Web Docs — JavaScriptガイド、およびString・Array・Set・Object.entriesのリファレンス
- RFC 4180 — CSV形式(カンマ・ダブルクォート・改行の規則)についての一般的な説明
学習目標をすべて達成できたら完了を押しましょう。
このブラウザでは保存できないため、この画面でのみ表示されます。