01. スネークケースとキャメルケースを変換する
user_id と userId を行き来させます。データベースの列名と JSON のキー、定数と変数のように、名前の書き方をそろえるときに使います。HTTPServer のように大文字が続く名前で、よく見かける書き方はほとんどの言語で崩れます。 ほかの言語・正解の例・解説 →
case_convert.js24.15.0 で 14 / 14 件の出力が正解と一致
// スネークケース(user_id)とキャメルケース(userId)を、指定した向きに変換する
const NG = "NG: 半角の英数字と _ だけにしてください";
const [mode, s] = process.argv.slice(2);
// JavaScript の \w は ASCII だけだが、言語によってはひらがなにも合う。どの言語でも同じ意味になる [A-Za-z0-9_] と書く
if (!/^[A-Za-z0-9_]*$/.test(s)) {
console.log(NG);
} else if (mode === "snake") {
// 切れ目は2種類。小文字か数字の次の大文字(userId)と、大文字の続きの最後で次が小文字のもの(HTTP|Server)
// /([A-Z])/g を "_$1" にするだけだと HTTPServer が _h_t_t_p_server になる
const out = s
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
.replace(/([A-Z])(?=[A-Z][a-z])/g, "$1_");
console.log(out.toLowerCase());
} else {
// split("_") は「__」のところで空の部分を作る。filter で捨てる
const parts = s.split("_").filter((p) => p !== "");
console.log(
parts
.map((p, i) => (i === 0 ? p.toLowerCase() : p[0].toUpperCase() + p.slice(1).toLowerCase()))
.join("")
);
}
02. HTML の特殊文字をエスケープする
利用者が入力した文字を HTML に埋め込む前に、< > & " ' を < などに置き換えます。標準の関数は言語ごとに ' の書き方が違い、この決まりの形とそのまま一致したのは Ruby だけでした。 ほかの言語・正解の例・解説 →
escape.js24.15.0 で 8 / 8 件の出力が正解と一致
// 受け取った文字列の & < > " ' を、HTML の書き方(& < > " ')に変える
const s = process.argv[2];
// JavaScript の標準にはエスケープの関数がないので、自分で置き換える。
// 文字列を渡す replace は最初の1つしか変えないので、g を付けた正規表現で5文字をまとめて1回で置き換える
const map = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
console.log(s.replace(/[&<>"']/g, (c) => map[c]));
03. 順番を保って重複を消す
同じ値の2回目以降を消し、初めて出た順に残します。タグの一覧や、選んだ項目の一覧でよく使います。集合(Set)に入れて戻す書き方では、言語によって順番が変わったり、実行するたびに変わったりします。 ほかの言語・正解の例・解説 →
unique.js24.15.0 で 9 / 9 件の出力が正解と一致
// 受け取った文字列から、2回目以降に出たものを消し、初めて出た順に出す
const items = process.argv.slice(2);
// Set は入れた順を覚えている。オブジェクトのキーで覚えると "1" "3" のような整数に見えるキーが小さい順に前へ並ぶ
const out = [...new Set(items)];
for (const s of out) {
console.log(s === "" ? "(空)" : s);
}
console.log(`${out.length}件(${items.length - out.length}件を消した)`);
04. 日付と時刻を ISO 8601 で読み書きする
API やログでよく使う 2026-10-30T09:00:00+09:00 の形を読み、UTC と日本時間で書き直します。標準のパーサーにそのまま渡すと、2月30日を3月2日として読んだり、タイムゾーンの無い日時を動かす場所の時間帯で読んだりします。 ほかの言語・正解の例・解説 →
iso8601.js24.15.0 で 15 / 15 件の出力が正解と一致
// ISO 8601 の日時を受け取って、UTC と日本時間で書き出す
// new Date(s) は 2月30日を3月2日に、タイムゾーンの無い日時を動かす場所の時間帯で読む。
// 形は正規表現で決めて、数字を取り出して自分で組み立てる
const ISO = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})(\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})?$/;
function convert(s) {
const m = ISO.exec(s);
if (!m) return "NG: 日時の形ではありません";
const [y, mo, d, h, mi, se] = m.slice(1, 7).map(Number);
// Date.UTC は 0〜99年を1900年代にするので、setUTCFullYear で年をそのまま入れる。
// どちらも 2月30日を3月2日に、25時を翌日1時に繰り上げるので、作った値が入力どおりかを確かめる
const t = new Date(0);
t.setUTCFullYear(y, mo - 1, d);
t.setUTCHours(h, mi, se);
const local = t.getTime();
if (t.getUTCFullYear() !== y || t.getUTCMonth() !== mo - 1 || t.getUTCDate() !== d || t.getUTCHours() !== h || t.getUTCMinutes() !== mi || t.getUTCSeconds() !== se) {
return "NG: 日時の形ではありません";
}
const tz = m[8];
if (tz === undefined) return "NG: タイムゾーン(Z か +09:00 など)を付けてください";
let offsetMin = 0;
if (tz !== "Z") {
const oh = Number(tz.slice(1, 3)), om = Number(tz.slice(4, 6));
if (oh > 23 || om > 59) return "NG: 日時の形ではありません";
offsetMin = (tz[0] === "+" ? 1 : -1) * (oh * 60 + om);
}
// 書いてある時刻から時差を引くと UTC。日本時間は UTC に9時間足して、getUTC* で読む(getHours などは動かす場所の時間帯)
const utc = new Date(local - offsetMin * 60000);
const jst = new Date(utc.getTime() + 9 * 3600000);
const p = (n, w = 2) => String(n).padStart(w, "0");
return `UTC ${utc.toISOString().slice(0, 19)}Z\n` +
`日本時間 ${p(jst.getUTCFullYear(), 4)}年${p(jst.getUTCMonth() + 1)}月${p(jst.getUTCDate())}日 ${p(jst.getUTCHours())}:${p(jst.getUTCMinutes())}:${p(jst.getUTCSeconds())}`;
}
console.log(convert(process.argv[2]));
05. アクセスログを集計する
Web サーバーのアクセスログを標準入力から読み、ステータスごとの件数と、応答の遅いリクエストの上位3件を出します。空白で区切って何番目かを取る書き方は手軽ですが、壊れた行を数えたり、応答秒を文字のまま並べたりします。 ほかの言語・正解の例・解説 →
access_log.js24.15.0 で 8 / 8 件の出力が正解と一致
// 標準入力のアクセスログを読んで、件数・ステータスごとの件数・遅い順の上位3件を出す
// 日時やHTTPの版には空白が入るので、空白で区切って何番目と数えず、行の形そのものを正規表現で確かめる
const LOG = /^\S+ \S+ \S+ \[[^\]]+\] "(\S+) (\S+) [^"]+" ([0-9]{3}) ([0-9]+|-) ([0-9]+\.[0-9]+)$/;
const rows = [];
let bad = 0;
// \r\n と \n のどちらでも区切る。空の入力は [""] になり、下で空の行として飛ばされる
for (const line of require("fs").readFileSync(0, "utf8").split(/\r?\n/)) {
if (line === "") continue;
const m = LOG.exec(line);
if (m === null) {
bad++;
continue;
}
rows.push({ sec: m[5], method: m[1], path: m[2], status: m[3] });
}
console.log(`件数 ${rows.length}`);
if (bad > 0) console.log(`読めない行 ${bad}`);
// Map は入れた順に回る。並べる順は自分で決める
const counts = new Map();
for (const r of rows) counts.set(r.status, (counts.get(r.status) ?? 0) + 1);
console.log("ステータス");
// 引数なしの sort は文字として並べる。ステータスは3桁そろいなので、これで数の小さい順になる
for (const status of [...counts.keys()].sort()) console.log(`${status} ${counts.get(status)}`);
console.log("遅い順");
// 応答秒は Number にして引き算で比べる。sort は ES2019 から安定と決まった(同じ秒なら先に出た行が先)。
// toSorted は元の配列を変えずに並べ替えた新しい配列を返す
const slow = rows.toSorted((a, b) => Number(b.sec) - Number(a.sec)).slice(0, 3);
for (const r of slow) console.log(`${r.sec} ${r.method} ${r.path}`);
06. URL 用の文字列(slug)を作る
記事のタイトルから「hello-world」のような URL の一部を作ります。ブログや商品ページのアドレスを、見て意味が分かる形にするときに使います。よく見かける書き方は、日本語のタイトルをそのまま URL に入れたり、空の slug を黙って返したりします。 ほかの言語・正解の例・解説 →
slug.js24.15.0 で 14 / 14 件の出力が正解と一致
// 文字列を URL に使える形(slug)に変える
const MAX = 32;
const NG = "NG: URL に使える文字がありません";
// toLowerCase() は Unicode の決まりで変えるので、İ が2文字になる。A〜Z だけを小文字にする
let s = process.argv[2].replace(/[A-Z]/g, (c) => c.toLowerCase());
// \W は [^A-Za-z0-9_] なので「_」が残る。残す文字を [a-z0-9] と書く
s = s.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
if (s.length > MAX) {
s = s.slice(0, MAX).replace(/-+$/, "");
}
console.log(s === "" ? NG : s);
07. URL エンコードする
検索語や日本語の名前を、URL のクエリの値(`?q=...`)に入れられる形に変えます。よく見かける書き方は、空白を「+」にしたり、`!` や `~` を変えずに残したりして、受け取る側で別の文字列になります。 ほかの言語・正解の例・解説 →
url_encode.js24.15.0 で 15 / 15 件の出力が正解と一致
// 文字列を URL のクエリの値に入れられる形(パーセントエンコード)に変える // encodeURIComponent は ! * ' ( ) の5つを残す(RFC 3986 では変える文字)。その5つだけ自分で変える const encoded = encodeURIComponent(process.argv[2]).replace(/[!*'()]/g, (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(), ); console.log(encoded);