なぜ「単語分割」ユーティリティが必要になるのか
キャメルケース化・スネークケース化・ケバブケース化・パスカルケース化。
ここまでいろいろやってきましたが、実は全部の“土台”にあるのが 「単語分割」 です。
"userNameId""user_name_id""user-name-id""user name id"
これらを「user」「name」「id」という“単語の配列”に分解できれば、
あとは「どうつなげ直すか」を変えるだけで、
キャメル・スネーク・ケバブ・パスカルに自由に変換できます。
だからこそ、「単語分割ユーティリティ」は
文字列整形系ユーティリティの“心臓部”と言っていい存在です。
単語分割でやりたいことを整理する
いろんな形式から「単語の配列」を取り出したい
業務でよく出てくる形式を並べてみます。
"userNameId"(キャメルケース)"UserNameId"(パスカルケース)"user_name_id"(スネークケース)"user-name-id"(ケバブケース)"user name id"(スペース区切り)
これらを全部、最終的にこうしたいわけです。
["user", "name", "id"]
JavaScriptつまり、「入力形式がバラバラでも、同じ“単語の配列”に落とし込む」のがゴールです。
単語分割の基本方針
やりたいことをもう少し細かく分解すると、こうなります。
前後の余計な空白を削る。
スペース・アンダースコア・ハイフンは「区切り」として扱う。
キャメルケース/パスカルケースでは「大文字の手前」で区切る。
全部いったん小文字にそろえておく(後で整形しやすくするため)。
このルールを一箇所にまとめたのが「単語分割ユーティリティ」です。
実装例:単語分割ユーティリティの基本形
コード全体
まずは、完成形を見てください。
function splitWords(value) {
if (value == null) return [];
const str = String(value).trim();
if (str === "") return [];
// 1. 区切り文字(スペース・アンダースコア・ハイフン)を統一
const normalized = str.replace(/[\s_\-]+/g, " ");
// 2. キャメルケース/パスカルケースの大文字の前にスペースを入れる
const withSpaces = normalized
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
// 3. スペースで分割して、小文字にそろえる
const words = withSpaces
.split(" ")
.map((w) => w.toLowerCase())
.filter(Boolean);
return words;
}
JavaScriptここから、重要なポイントを順番に深掘りしていきます。
ステップ1:区切り文字を「スペース」に統一する
スネーク・ケバブ・スペースを一旦まとめる
最初のポイントはここです。
const normalized = str.replace(/[\s_\-]+/g, " ");
JavaScript[\s_\-]+ は、
空白文字(スペース・タブ・改行など)
アンダースコア _
ハイフン -
が「1文字以上連続した部分」を意味します。
それを " "(半角スペース1つ)に置き換えています。
例えば:
"user_name-id" → "user name id"" user__name---id " → " user name id "
という感じです。
ここで大事なのは、
「いろんな区切り記号を“スペース1つ”に正規化している」ことです。
こうしておくと、後の処理が一気にシンプルになります。
ステップ2:キャメル/パスカルの「大文字の手前」にスペースを入れる
大文字を「単語の境目」として扱う
次のポイントがここです。
const withSpaces = normalized
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
.replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
JavaScriptやっていることを噛み砕きます。
([a-z0-9])([A-Z])
小文字 or 数字の直後に大文字が来たら、その間にスペースを入れる。
例: "userName" → "user Name"、"user1Name" → "user1 Name"
([A-Z])([A-Z][a-z])
大文字が連続していて、そのあとに小文字が続くところで区切る。
例: "UserID" → "User ID"、"HTTPRequest" → "HTTP Request"
これを、さっきの「区切りをスペースに統一した文字列」に対して行うことで、
"userNameId" → "user Name Id""UserID" → "User ID"
のように、「単語の境目」にスペースを差し込んでいきます。
ここが キャメル/パスカルを“単語”として扱えるようにする一番重要な部分 です。
ステップ3:スペースで分割して、小文字にそろえる
最終的に「単語の配列」に落とし込む
最後の仕上げがここです。
const words = withSpaces
.split(" ")
.map((w) => w.toLowerCase())
.filter(Boolean);
JavaScriptsplit(" ") でスペース区切りにして配列にする。toLowerCase() で全部小文字にそろえる。filter(Boolean) で空文字("")を取り除く。
これで、どんな形式の入力でも、
最終的に「小文字の単語配列」に変換できます。
具体例で動きを確認する
いろんな形式を同じ配列に落とし込む
splitWords("userNameId"); // ["user", "name", "id"]
splitWords("UserNameId"); // ["user", "name", "id"]
splitWords("user_name_id"); // ["user", "name", "id"]
splitWords("user-name-id"); // ["user", "name", "id"]
splitWords(" user name id "); // ["user", "name", "id"]
splitWords("USER_ID"); // ["user", "id"]
JavaScript形式がバラバラでも、
同じ配列 ["user", "name", "id"] にそろえられているのが分かると思います。
ここまで来れば、あとは
キャメルケース化 → 最初を小文字、2単語目以降の先頭を大文字にしてつなげる。
パスカルケース化 → 全部の先頭を大文字にしてつなげる。
スネークケース化 → _ でつなげる。
ケバブケース化 → - でつなげる。
という「再構成」の問題に変わります。
単語分割ユーティリティをどう活かすか
他の整形ユーティリティの“共通基盤”にする
例えば、キャメルケース化をこう書き換えられます。
function toCamelCase(value) {
const words = splitWords(value);
if (words.length === 0) return "";
const [first, ...rest] = words;
return (
first +
rest
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join("")
);
}
JavaScriptパスカルケース化ならこうです。
function toPascalCase(value) {
const words = splitWords(value);
if (words.length === 0) return "";
return words
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join("");
}
JavaScriptスネークケース化・ケバブケース化も同じように書けます。
「単語分割を一箇所にまとめる」ことで、
他の整形ユーティリティがシンプルかつ一貫した動きになる
ここが、業務コードとしてめちゃくちゃ大事なポイントです。
設計として意識してほしいこと
「まず単語に分ける」という発想を持つ
文字列整形でいきなり
「大文字を小文字にして…」
「アンダースコアをハイフンにして…」
とやり始めると、だいたい途中でぐちゃぐちゃになります。
そうではなくて、
「まず単語に分ける」 → 「どうつなげ直すかを決める」
という二段構えで考える癖をつけると、
処理の見通しが一気に良くなります。
単語分割ユーティリティは、その「最初の一段目」を担う存在です。
小さな練習で感覚をつかむ
ブラウザのコンソールで、いろいろ試してみてください。
splitWords("userName");
splitWords("UserNameId");
splitWords("user_name_id");
splitWords("user-name-id");
splitWords(" USER NAME ID ");
splitWords(null);
JavaScript全部が「小文字の単語配列」に落ちていくのを確認してみてください。
そのうえで、自分のプロジェクトに
export function splitWords(value) { ... }
JavaScriptを一つ置いて、
「命名変換系のユーティリティは、必ず最初に splitWords を通る」
というルールにしてみてください。
それができた瞬間、あなたの文字列整形は
「場当たり的な置換の寄せ集め」から
「単語分割 → 再構成」という、筋の通った設計に変わります。
