業務・実務で使えるJavaScriptユーティリティ:kebab-caseに変換する方法
プログラミングの現場では、「UserName」のようなパスカルケースや「userName」のようなキャメルケースを、「user-name」のようなケバブケースに変換する必要が頻繁に生じます。今回は、様々な形式の文字列をケバブケースに統一するユーティリティ関数について、ステップバイステップで解説します。
なぜkebab-caseに変換する必要があるのか
実務では、URLのパス、CSSクラス名、ファイル名、HTML属性値など、特定の識別子にケバブケースを使用する場面が多くあります。特にWeb開発では、URLの可読性を高めるためや、CSSの命名規則として、ケバブケースが広く採用されています。
例えば、JavaScriptのコード内で「userName」という変数を使用している場合、URLパラメータやCSSクラス名としては「user-name」に変換する必要があります。また、APIのレスポンスで受け取った「UserName」を、フロントエンドのルーティング用の「user-name」に変換したい場合などが典型的です。
具体的な使用場面
- JavaScriptの変数名をURLパラメータに変換(例:「userName」→「user-name」)
- コンポーネント名をCSSクラス名に変換(例:「UserProfile」→「user-profile」)
- ファイル名やディレクトリ名の生成(例:「UserService」→「user-service」)
- HTML属性値の生成(例:「dataValue」→「data-value」)
- ルーティングパスの生成(例:「OrderHistory」→「order-history」)
基本的な実装方法
ケバブケースへの変換は、主に以下の3つのパターンに対応する必要があります。それぞれのパターンを段階的に実装していきましょう。
パターン1:キャメルケースからケバブケースへの変換
大文字で区切られた文字列を、ハイフン(-)で区切り、すべて小文字に変換する方法です。
// キャメルケースをケバブケースに変換する関数
function camelToKebab(str) {
// 空文字やundefinedの場合はそのまま返す(エラー防止)
if (!str) return str;
// 大文字の前にハイフンを挿入し、すべて小文字に変換
return str
.replace(/([A-Z])/g, '-$1') // 大文字の前に-を追加
.toLowerCase() // すべて小文字に変換
.replace(/^-/, ''); // 先頭のハイフンを削除(最初の文字が大文字の場合)
}
// 使用例
console.log(camelToKebab('userName')); // 'user-name'
console.log(camelToKebab('userEmail')); // 'user-email'
console.log(camelToKebab('orderId')); // 'order-id'
console.log(camelToKebab('user')); // 'user'(変換不要な場合はそのまま)JavaScriptコードの解説
この関数は3つのステップで動作します。
- ハイフンの挿入:
replace(/([A-Z])/g, '-$1')で、すべての大文字の前にハイフンを挿入します。$1はキャプチャした大文字を表します。 - 小文字への変換:
toLowerCase()で文字列全体を小文字に変換します。 - 先頭のハイフン削除:
replace(/^-/, '')で、もし先頭にハイフンがあれば削除します。これは「UserName」のようなパスカルケースに対応するためです。
パターン2:パスカルケースからケバブケースへの変換
キャメルケースとほぼ同様ですが、先頭が大文字の場合に対応する必要があります。
// パスカルケースをケバブケースに変換する関数
function pascalToKebab(str) {
// 空文字やundefinedの場合はそのまま返す
if (!str) return str;
// 先頭の大文字を小文字に変換し、その後キャメルケースと同じ処理
return str
.replace(/([A-Z])/g, '-$1') // 大文字の前に-を追加
.toLowerCase() // すべて小文字に変換
.replace(/^-/, ''); // 先頭のハイフンを削除
}
// 使用例
console.log(pascalToKebab('UserName')); // 'user-name'
console.log(pascalToKebab('UserEmail')); // 'user-email'
console.log(pascalToKebab('OrderId')); // 'order-id'JavaScript連続した大文字への対応
「HTMLParser」のような略語を含む文字列の場合、単純な変換では「h-t-m-l-parser」のような不自然な結果になります。より洗練された変換が必要な場合は、以下のような対応を検討してください。
// より洗練されたケバブケース変換(連続大文字への配慮版)
function pascalToKebabAdvanced(str) {
if (!str) return str;
return str
// 連続する大文字の区切りを適切に処理(HTMLParser → html-parser)
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
// 小文字と大文字の区切りを処理(HtmlParser → html-parser)
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
// すべて小文字に変換
.toLowerCase();
}
// 使用例
console.log(pascalToKebabAdvanced('UserName')); // 'user-name'
console.log(pascalToKebabAdvanced('HTMLParser')); // 'html-parser'(自然な結果)
console.log(pascalToKebabAdvanced('URL')); // 'url'(すべて大文字の場合)JavaScriptパターン3:スネークケースからケバブケースへの変換
アンダースコア(_)で区切られた文字列を、ハイフン(-)に置き換え、すべて小文字に変換する方法です。
// スネークケースをケバブケースに変換する関数
function snakeToKebab(str) {
// 空文字やundefinedの場合はそのまま返す
if (!str) return str;
// アンダースコアをハイフンに置き換え、小文字に変換
return str
.replace(/_/g, '-') // アンダースコアをハイフンに置き換え
.toLowerCase(); // すべて小文字に変換
}
// 使用例
console.log(snakeToKebab('user_name')); // 'user-name'
console.log(snakeToKebab('user_email')); // 'user-email'
console.log(snakeToKebab('order_id')); // 'order-id'JavaScript汎用的なkebab-case変換関数の実装
実務では、どの形式で入力されるか分からない場合があります。そこで、キャメルケース・パスカルケース・スネークケースのいずれにも対応できる汎用的な関数を作成しましょう。
統合版ケバブケース変換関数
/**
* 任意の文字列をケバブケースに変換する汎用関数
* @param {string} str - 変換対象の文字列
* @returns {string} ケバブケースに変換された文字列
*/
function toKebabCase(str) {
// 入力チェック(null、undefined、空文字対策)
if (!str || typeof str !== 'string') {
return '';
}
// ステップ1: 文字列の正規化
let result = str
// 連続する大文字の区切りを適切に処理(HTMLParser → html-parser)
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
// 小文字と大文字の区切りを処理(userName → user-name)
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
// アンダースコアとスペースをハイフンに置き換え
.replace(/[_\s]+/g, '-')
// 連続するハイフンを1つにまとめる
.replace(/-+/g, '-')
// 先頭と末尾のハイフンを削除
.replace(/^-+|-+$/g, '');
// ステップ2: すべて小文字に変換
return result.toLowerCase();
}
// 使用例
console.log(toKebabCase('userName')); // 'user-name'
console.log(toKebabCase('UserName')); // 'user-name'
console.log(toKebabCase('user_name')); // 'user-name'
console.log(toKebabCase('user name')); // 'user-name'
console.log(toKebabCase('USER_NAME')); // 'user-name'
console.log(toKebabCase('user-name')); // 'user-name'(既にケバブケース)
console.log(toKebabCase('HTMLParser')); // 'html-parser'
console.log(toKebabCase('userId123')); // 'user-id-123'
console.log(toKebabCase(' user name ')); // 'user-name'(スペースも処理)JavaScriptコードの解説
この関数は複数の正規表現を順番に適用して、文字列を正規化していきます。
- 連続大文字の処理:
([A-Z]+)([A-Z][a-z])で、「HTMLParser」のような連続する大文字を適切に区切ります。 - キャメルケースの処理:
([a-z\d])([A-Z])で、「userName」のような小文字と大文字の区切りを処理します。 - アンダースコアとスペースの処理:
[_\s]+で、アンダースコアやスペースをハイフンに置き換えます。 - ハイフンの正規化:連続するハイフンを1つにまとめ、先頭と末尾のハイフンを削除します。
- 小文字への変換:最後にすべて小文字に変換します。
重要な注意点:正規表現の順序
上記の正規表現は順序が重要です。連続大文字の処理を先に行わないと、「HTMLParser」が「h-t-m-l-parser」のような不自然な結果になってしまいます。正規表現を組み合わせる場合は、処理の順序を慎重に検討してください。
より実践的な応用パターン
実際の業務では、単一の文字列だけでなく、オブジェクトのキーや配列の要素を一括で変換する場面が多くあります。
1. オブジェクトのキーを一括変換
APIから受け取ったオブジェクトのすべてのキーをケバブケースに変換したい場合、以下のような実装が有効です。
/**
* オブジェクトのキーをすべてケバブケースに変換
* @param {Object} obj - 変換対象のオブジェクト
* @returns {Object} キーがケバブケースに変換された新しいオブジェクト
*/
function objectKeysToKebab(obj) {
// オブジェクトでない場合はそのまま返す
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 配列の場合は各要素に再帰的に適用
if (Array.isArray(obj)) {
return obj.map(item => objectKeysToKebab(item));
}
// オブジェクトの各キーを変換
return Object.keys(obj).reduce((acc, key) => {
// キーをケバブケースに変換
const kebabKey = toKebabCase(key);
// 値もオブジェクトの場合は再帰的に変換
acc[kebabKey] = objectKeysToKebab(obj[key]);
return acc;
}, {});
}
// 使用例
const apiResponse = {
userName: 'Taro',
userEmail: '[email protected]',
addressInfo: {
zipCode: '123-4567',
cityName: 'Tokyo'
},
orderHistory: [
{ orderId: 1, orderDate: '2024-01-01' },
{ orderId: 2, orderDate: '2024-01-02' }
]
};
const kebabCased = objectKeysToKebab(apiResponse);
console.log(kebabCased);
// 結果:
// {
// 'user-name': 'Taro',
// 'user-email': '[email protected]',
// 'address-info': {
// 'zip-code': '123-4567',
// 'city-name': 'Tokyo'
// },
// 'order-history': [
// { 'order-id': 1, 'order-date': '2024-01-01' },
// { 'order-id': 2, 'order-date': '2024-01-02' }
// ]
// }JavaScript再帰処理の重要性
上記の関数は「再帰(自分自身を呼び出す処理)」を使用しています。これにより、ネストされたオブジェクトや配列の中身も完全に変換できます。実務では、APIのレスポンスが深くネストされていることが多いため、この再帰的なアプローチが非常に重要です。
2. 配列内の文字列を一括変換
// 配列内のすべての文字列をケバブケースに変換
function arrayToKebab(arr) {
if (!Array.isArray(arr)) return arr;
return arr.map(item => {
if (typeof item === 'string') {
return toKebabCase(item);
}
return item; // 文字列でない場合はそのまま
});
}
// 使用例
const columns = ['userName', 'userEmail', 'orderId'];
const kebabColumns = arrayToKebab(columns);
console.log(kebabColumns); // ['user-name', 'user-email', 'order-id']JavaScript3. 安全な文字列変換(null・undefined対策)
実務では、予期せぬタイミングでnullやundefinedが渡されることがあります。より堅牢な関数を作成しましょう。
// 型チェックを厳密に行う安全な関数
function safeToKebab(str) {
// 文字列型でない、または空文字の場合は空文字を返す
if (typeof str !== 'string' || str.length === 0) {
return '';
}
// 前後のスペースを除去してから変換
const trimmed = str.trim();
return toKebabCase(trimmed);
}
// 使用例
console.log(safeToKebab(' userName ')); // 'user-name'(スペースも除去)
console.log(safeToKebab(null)); // ''(エラーにならない)
console.log(safeToKebab(123)); // ''(数値でもエラーにならない)
console.log(safeToKebab(undefined)); // ''(エラーにならない)JavaScriptよくある間違いと注意点
初心者の方がつまずきやすいポイントを3つ挙げます。
1. 文字列の変更不可性を理解していない
JavaScriptの文字列は「イミュータブル(変更不可)」です。つまり、replace()やtoLowerCase()を実行しても元の文字列は変わらず、新しい文字列が返されるだけです。以下のコードは間違いです。
// 間違った例
let str = 'userName';
str.replace(/([A-Z])/g, '-$1');
str.toLowerCase();
console.log(str); // 依然として 'userName'
// 正しい例
let str = 'userName';
str = str.replace(/([A-Z])/g, '-$1').toLowerCase();
console.log(str); // 'user-name'JavaScript2. 連続した大文字の扱い
「HTMLParser」や「URL」のような連続した大文字を含む文字列は、単純な変換では意図しない結果になることがあります。以下の例を確認してください。
// 単純な変換の問題点
function simpleCamelToKebab(str) {
return str.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
}
console.log(simpleCamelToKebab('userName')); // 'user-name'(問題なし)
console.log(simpleCamelToKebab('HTMLParser')); // 'h-t-m-l-parser'(不自然)
// 対策:連続する大文字を適切に処理
function advancedCamelToKebab(str) {
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
.toLowerCase();
}
console.log(advancedCamelToKebab('userName')); // 'user-name'(自然)
console.log(advancedCamelToKebab('HTMLParser')); // 'html-parser'(自然)JavaScript3. 日本語を含む文字列への対応
日本語には大文字・小文字の概念がないため、日本語を含む文字列の場合は注意が必要です。以下のコードを確認してください。
// 日本語を含む場合
console.log(toKebabCase('ユーザー名')); // 'ユーザー名'(変化なし)
console.log(toKebabCase('userName名前')); // 'user-name名前'(日本語はそのまま)
console.log(toKebabCase('user_name_テスト')); // 'user-name-テスト'(日本語はそのまま)JavaScript日本語を含む場合でも、基本的には区切り文字(大文字やアンダースコア)で区切られた部分が正しく処理されます。ただし、日本語同士が連続している場合は、単語の区切りとして認識されないため、意図した結果にならないことがあります。
実務で使えるテンプレート集
コピー&ペーストですぐに使える形でまとめました。プロジェクトの共通ユーティリティファイルに保存しておくと便利です。
/**
* 文字列ケース変換ユーティリティ集
* 業務・実務で使用する文字列操作の関数群
*/
const CaseConverter = {
/**
* キャメルケースをケバブケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} ケバブケースに変換された文字列
*/
camelToKebab: function(str) {
if (!str || typeof str !== 'string') return '';
return str
.replace(/([A-Z])/g, '-$1')
.toLowerCase()
.replace(/^-/, '');
},
/**
* パスカルケースをケバブケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} ケバブケースに変換された文字列
*/
pascalToKebab: function(str) {
if (!str || typeof str !== 'string') return '';
return str
.replace(/([A-Z])/g, '-$1')
.toLowerCase()
.replace(/^-/, '');
},
/**
* スネークケースをケバブケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} ケバブケースに変換された文字列
*/
snakeToKebab: function(str) {
if (!str || typeof str !== 'string') return '';
return str
.replace(/_/g, '-')
.toLowerCase();
},
/**
* 任意の文字列をケバブケースに変換(汎用版)
* @param {string} str - 変換対象の文字列
* @returns {string} ケバブケースに変換された文字列
*/
toKebabCase: function(str) {
if (!str || typeof str !== 'string') return '';
return str
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
.replace(/([a-z\d])([A-Z])/g, '$1-$2')
.replace(/[_\s]+/g, '-')
.replace(/-+/g, '-')
.replace(/^-+|-+$/g, '')
.toLowerCase();
},
/**
* オブジェクトのキーをすべてケバブケースに変換
* @param {Object} obj - 変換対象のオブジェクト
* @returns {Object} キーがケバブケースに変換された新しいオブジェクト
*/
objectKeysToKebab: function(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (Array.isArray(obj)) {
return obj.map(item => this.objectKeysToKebab(item));
}
return Object.keys(obj).reduce((acc, key) => {
const kebabKey = this.toKebabCase(key);
acc[kebabKey] = this.objectKeysToKebab(obj[key]);
return acc;
}, {});
}
};
// 使用例
console.log(CaseConverter.camelToKebab('userName')); // 'user-name'
console.log(CaseConverter.pascalToKebab('UserName')); // 'user-name'
console.log(CaseConverter.snakeToKebab('user_name')); // 'user-name'
console.log(CaseConverter.toKebabCase('userName')); // 'user-name'
console.log(CaseConverter.toKebabCase('UserName')); // 'user-name'
console.log(CaseConverter.toKebabCase('user_name')); // 'user-name'
// オブジェクトの変換例
const data = { userName: 'Taro', addressInfo: { zipCode: '123' } };
console.log(CaseConverter.objectKeysToKebab(data));
// { 'user-name': 'Taro', 'address-info': { 'zip-code': '123' } }JavaScriptオブジェクト化するメリット
関数をオブジェクト(CaseConverter)にまとめることで、以下のような利点があります。
- 名前空間の汚染を防ぐ:グローバル変数と競合しにくくなります。
- コードの見通しが良くなる:
CaseConverter.camelToKebab()と書くことで、文字列のケース変換であることが明確になります。 - 保守性が向上する:関連する関数が一箇所に集約されるため、修正や追加が容易になります。
- thisによる相互参照:
objectKeysToKebab内でthis.toKebabCaseを呼び出すことで、同じオブジェクト内の他のメソッドを利用できます。
まとめ
ケバブケースへの変換は、URL設計やCSS命名、ファイル名生成など、フロントエンド開発で特に重要な処理です。今回紹介したように、キャメルケース・パスカルケース・スネークケースという異なる形式から、一貫したケバブケースへの変換を実現できます。
特に重要なのは、「入力値の検証を必ず行う」こと、「文字列は変更不可であることを理解する」こと、そして「連続した大文字や日本語などの特殊なケースへの配慮」の3点です。これらを押さえておけば、実務で発生する様々なケースに対応できるようになります。
まずは基本形のcamelToKebabやpascalToKebabから使い始め、必要に応じて汎用的なtoKebabCaseやオブジェクト一括変換に拡張していくことをお勧めします。チームで共有するユーティリティとして整備することで、コードの一貫性と生産性が大幅に向上するはずです。

