業務・実務で使えるJavaScriptユーティリティ:camelCaseに変換する方法
プログラミングの現場では、「user_name」のようなスネークケースや「UserName」のようなパスカルケースを、「userName」のようなキャメルケースに変換する必要が頻繁に生じます。今回は、様々な形式の文字列をキャメルケースに統一するユーティリティ関数について、ステップバイステップで解説します。
なぜcamelCaseに変換する必要があるのか
実務では、異なるシステム間でデータをやり取りする際、命名規則の違いが大きな課題となります。データベースのカラム名は「user_name」(スネークケース)、APIのレスポンスは「UserName」(パスカルケース)、そしてJavaScriptの変数名は「userName」(キャメルケース)と、それぞれ異なる形式を採用していることが一般的です。
特にJavaScriptでは、変数名・関数名・プロパティ名にキャメルケースを使用することが標準的な命名規則とされています。**MDN Web Docs**においても、JavaScriptの識別子にはキャメルケースを使用することが推奨されています。
具体的な使用場面
- データベースのカラム名をJavaScriptの変数名に変換(例:「user_name」→「userName」)
- APIレスポンスのフィールド名を統一(例:「UserName」→「userName」)
- URLのクエリパラメータをオブジェクトのプロパティに変換(例:「user-id」→「userId」)
- フォームのname属性をJavaScriptの変数名にマッピング(例:「user-email」→「userEmail」)
基本的な実装方法
キャメルケースへの変換は、主に以下の3つのパターンに対応する必要があります。それぞれのパターンを段階的に実装していきましょう。
パターン1:スネークケースからキャメルケースへの変換
アンダースコア(_)で区切られた文字列を、単語の先頭を大文字にして連結する方法です。
// スネークケースをキャメルケースに変換する関数
function snakeToCamel(str) {
// 空文字やundefinedの場合はそのまま返す(エラー防止)
if (!str) return str;
// アンダースコアで分割し、各単語を処理
return str.replace(/_([a-z])/g, function(match, letter) {
// アンダースコアを削除し、次の文字を大文字に変換
return letter.toUpperCase();
});
}
// 使用例
console.log(snakeToCamel('user_name')); // 'userName'
console.log(snakeToCamel('user_email')); // 'userEmail'
console.log(snakeToCamel('order_id')); // 'orderId'
console.log(snakeToCamel('user')); // 'user'(変換不要な場合はそのまま)JavaScriptコードの解説
この関数は正規表現を使用して、以下のステップで動作します。
- 正規表現のパターン:
_([a-z])は「アンダースコアの後に小文字が続く」部分にマッチします。 - キャプチャグループ:
([a-z])で、アンダースコアの後の文字をキャプチャ(捕捉)します。 - 置換処理:マッチした部分を、キャプチャした文字の大文字に置き換えます。これにより、アンダースコアは削除され、次の文字が大文字になります。
パターン2:パスカルケースからキャメルケースへの変換
先頭文字だけを小文字にするシンプルな変換です。
// パスカルケースをキャメルケースに変換する関数
function pascalToCamel(str) {
// 空文字やundefinedの場合はそのまま返す
if (!str) return str;
// 先頭文字を小文字にし、残りと結合
return str.charAt(0).toLowerCase() + str.slice(1);
}
// 使用例
console.log(pascalToCamel('UserName')); // 'userName'
console.log(pascalToCamel('OrderId')); // 'orderId'
console.log(pascalToCamel('HTMLParser')); // 'hTMLParser'(連続大文字はこのまま)JavaScript連続した大文字への対応
「HTMLParser」のような略語を含む文字列の場合、単純に先頭を小文字にするだけでは「hTMLParser」となり、不自然な結果になります。より洗練された変換が必要な場合は、以下のような対応を検討してください。
// より洗練されたキャメルケース変換(連続大文字への配慮版)
function pascalToCamelAdvanced(str) {
if (!str) return str;
// 先頭の連続する大文字を除く、最初の大文字を小文字に
return str.replace(/^([A-Z]+)([A-Z][a-z])/, function(match, p1, p2) {
return p1.toLowerCase() + p2;
}).replace(/^([A-Z])/, function(match) {
return match.toLowerCase();
});
}
// 使用例
console.log(pascalToCamelAdvanced('UserName')); // 'userName'
console.log(pascalToCamelAdvanced('HTMLParser')); // 'htmlParser'(より自然な結果)
console.log(pascalToCamelAdvanced('URL')); // 'url'(すべて大文字の場合)JavaScriptパターン3:ケバブケースからキャメルケースへの変換
ハイフン(-)で区切られた文字列を変換する方法です。
// ケバブケースをキャメルケースに変換する関数
function kebabToCamel(str) {
// 空文字やundefinedの場合はそのまま返す
if (!str) return str;
// ハイフンで分割し、各単語を処理
return str.replace(/-([a-z])/g, function(match, letter) {
// ハイフンを削除し、次の文字を大文字に変換
return letter.toUpperCase();
});
}
// 使用例
console.log(kebabToCamel('user-name')); // 'userName'
console.log(kebabToCamel('user-email')); // 'userEmail'
console.log(kebabToCamel('order-id')); // 'orderId'JavaScript汎用的なcamelCase変換関数の実装
実務では、どの形式で入力されるか分からない場合があります。そこで、スネークケース・パスカルケース・ケバブケースのいずれにも対応できる汎用的な関数を作成しましょう。
統合版キャメルケース変換関数
/**
* 任意の文字列をキャメルケースに変換する汎用関数
* @param {string} str - 変換対象の文字列
* @returns {string} キャメルケースに変換された文字列
*/
function toCamelCase(str) {
// 入力チェック(null、undefined、空文字対策)
if (!str || typeof str !== 'string') {
return '';
}
// ステップ1: 単語の区切りを明確にするための正規表現
// アンダースコア、ハイフン、スペース、大文字(単語の区切り)を検出
const words = str.match(/[A-Z]{2,}(?![a-z])|[A-Z]?[a-z]+|[A-Z]|[0-9]+/g);
// 単語が見つからない場合はそのまま返す
if (!words) {
return str;
}
// ステップ2: 最初の単語は小文字、以降の単語は先頭を大文字に
return words.map((word, index) => {
// 最初の単語はすべて小文字に
if (index === 0) {
return word.toLowerCase();
}
// 2番目以降の単語は先頭を大文字、残りを小文字に
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}).join('');
}
// 使用例
console.log(toCamelCase('user_name')); // 'userName'
console.log(toCamelCase('user-name')); // 'userName'
console.log(toCamelCase('UserName')); // 'userName'
console.log(toCamelCase('user name')); // 'userName'
console.log(toCamelCase('USER_NAME')); // 'userName'
console.log(toCamelCase('userName')); // 'userName'(既にキャメルケース)
console.log(toCamelCase('html_parser')); // 'htmlParser'
console.log(toCamelCase('user_id_123')); // 'userId123'JavaScriptコードの解説
この関数は2つの主要なステップで動作します。
- 単語の分割:正規表現
[A-Z]{2,}(?![a-z])|[A-Z]?[a-z]+|[A-Z]|[0-9]+を使用して、文字列を意味のある単位に分割します。[A-Z]{2,}(?![a-z]):連続する大文字(HTML、URLなど)を1つの単語として認識[A-Z]?[a-z]+:大文字で始まり小文字が続く単語(User、Nameなど)[A-Z]:単独の大文字[0-9]+:数字の連続
- キャメルケースへの組み立て:最初の単語はすべて小文字に、2番目以降の単語は先頭を大文字にして連結します。
重要な注意点:正規表現の複雑さ
上記の正規表現は強力ですが、複雑です。業務では、以下のような判断が必要です。
- シンプルさを優先:入力形式が限定的(スネークケースのみなど)であれば、専用の関数を使用した方が保守しやすいです。
- パフォーマンス:極端に長い文字列や、大量の変換を行う場合は、正規表現の複雑さが影響する可能性があります。
- 可読性:チームのスキルレベルに応じて、コメントを充実させるか、シンプルな実装に留めるかを検討してください。
より実践的な応用パターン
実際の業務では、単一の文字列だけでなく、オブジェクトのキーや配列の要素を一括で変換する場面が多くあります。
1. オブジェクトのキーを一括変換
APIから受け取ったオブジェクトのすべてのキーをキャメルケースに変換したい場合、以下のような実装が有効です。
/**
* オブジェクトのキーをすべてキャメルケースに変換
* @param {Object} obj - 変換対象のオブジェクト
* @returns {Object} キーがキャメルケースに変換された新しいオブジェクト
*/
function objectKeysToCamel(obj) {
// オブジェクトでない場合はそのまま返す
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 配列の場合は各要素に再帰的に適用
if (Array.isArray(obj)) {
return obj.map(item => objectKeysToCamel(item));
}
// オブジェクトの各キーを変換
return Object.keys(obj).reduce((acc, key) => {
// キーをキャメルケースに変換
const camelKey = toCamelCase(key);
// 値もオブジェクトの場合は再帰的に変換
acc[camelKey] = objectKeysToCamel(obj[key]);
return acc;
}, {});
}
// 使用例
const apiResponse = {
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' }
]
};
const camelCased = objectKeysToCamel(apiResponse);
console.log(camelCased);
// 結果:
// {
// userName: 'Taro',
// userEmail: '[email protected]',
// addressInfo: {
// zipCode: '123-4567',
// cityName: 'Tokyo'
// },
// orderHistory: [
// { orderId: 1, orderDate: '2024-01-01' },
// { orderId: 2, orderDate: '2024-01-02' }
// ]
// }JavaScript再帰処理の重要性
上記の関数は「再帰(自分自身を呼び出す処理)」を使用しています。これにより、ネストされたオブジェクトや配列の中身も完全に変換できます。実務では、APIのレスポンスが深くネストされていることが多いため、この再帰的なアプローチが非常に重要です。
2. 配列内の文字列を一括変換
// 配列内のすべての文字列をキャメルケースに変換
function arrayToCamel(arr) {
if (!Array.isArray(arr)) return arr;
return arr.map(item => {
if (typeof item === 'string') {
return toCamelCase(item);
}
return item; // 文字列でない場合はそのまま
});
}
// 使用例
const columns = ['user_name', 'user_email', 'order_id'];
const camelColumns = arrayToCamel(columns);
console.log(camelColumns); // ['userName', 'userEmail', 'orderId']JavaScript3. 安全な文字列変換(null・undefined対策)
実務では、予期せぬタイミングでnullやundefinedが渡されることがあります。より堅牢な関数を作成しましょう。
// 型チェックを厳密に行う安全な関数
function safeToCamel(str) {
// 文字列型でない、または空文字の場合は空文字を返す
if (typeof str !== 'string' || str.length === 0) {
return '';
}
// 前後のスペースを除去してから変換
const trimmed = str.trim();
return toCamelCase(trimmed);
}
// 使用例
console.log(safeToCamel(' user_name ')); // 'userName'(スペースも除去)
console.log(safeToCamel(null)); // ''(エラーにならない)
console.log(safeToCamel(123)); // ''(数値でもエラーにならない)
console.log(safeToCamel(undefined)); // ''(エラーにならない)JavaScriptよくある間違いと注意点
初心者の方がつまずきやすいポイントを3つ挙げます。
1. 文字列の変更不可性を理解していない
JavaScriptの文字列は「イミュータブル(変更不可)」です。つまり、toLowerCase()やreplace()を実行しても元の文字列は変わらず、新しい文字列が返されるだけです。以下のコードは間違いです。
// 間違った例
let str = 'user_name';
str.replace(/_([a-z])/g, function(match, letter) {
return letter.toUpperCase();
});
console.log(str); // 依然として 'user_name'
// 正しい例
let str = 'user_name';
str = str.replace(/_([a-z])/g, function(match, letter) {
return letter.toUpperCase();
});
console.log(str); // 'userName'JavaScript2. 連続した大文字の扱い
「HTMLParser」や「URL」のような連続した大文字を含む文字列は、単純な変換では意図しない結果になることがあります。以下の例を確認してください。
// 単純なパスカルケース変換の問題点
function simplePascalToCamel(str) {
return str.charAt(0).toLowerCase() + str.slice(1);
}
console.log(simplePascalToCamel('HTMLParser')); // 'hTMLParser'(不自然)
console.log(simplePascalToCamel('URL')); // 'uRL'(不自然)
// 対策:連続する大文字を適切に処理
function advancedPascalToCamel(str) {
return str.replace(/^([A-Z]+)([A-Z][a-z])/, function(match, p1, p2) {
return p1.toLowerCase() + p2;
}).replace(/^([A-Z])/, function(match) {
return match.toLowerCase();
});
}
console.log(advancedPascalToCamel('HTMLParser')); // 'htmlParser'(自然)
console.log(advancedPascalToCamel('URL')); // 'url'(自然)JavaScript3. 日本語を含む文字列への対応
日本語には大文字・小文字の概念がないため、日本語を含む文字列の場合は注意が必要です。以下のコードを確認してください。
// 日本語を含む場合
console.log(toCamelCase('ユーザー名')); // 'ユーザー名'(変化なし)
console.log(toCamelCase('user_名前')); // 'user名前'(日本語はそのまま)
console.log(toCamelCase('user_name_テスト')); // 'userNameテスト'(日本語はそのまま)JavaScript日本語を含む場合でも、基本的には区切り文字(アンダースコアやハイフン)で区切られた部分が正しく処理されます。ただし、日本語同士が連続している場合は、単語の区切りとして認識されないため、意図した結果にならないことがあります。
実務で使えるテンプレート集
コピー&ペーストですぐに使える形でまとめました。プロジェクトの共通ユーティリティファイルに保存しておくと便利です。
/**
* 文字列ケース変換ユーティリティ集
* 業務・実務で使用する文字列操作の関数群
*/
const CaseConverter = {
/**
* スネークケースをキャメルケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} キャメルケースに変換された文字列
*/
snakeToCamel: function(str) {
if (!str || typeof str !== 'string') return '';
return str.replace(/_([a-z])/g, function(match, letter) {
return letter.toUpperCase();
});
},
/**
* パスカルケースをキャメルケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} キャメルケースに変換された文字列
*/
pascalToCamel: function(str) {
if (!str || typeof str !== 'string') return '';
return str.charAt(0).toLowerCase() + str.slice(1);
},
/**
* ケバブケースをキャメルケースに変換
* @param {string} str - 変換対象の文字列
* @returns {string} キャメルケースに変換された文字列
*/
kebabToCamel: function(str) {
if (!str || typeof str !== 'string') return '';
return str.replace(/-([a-z])/g, function(match, letter) {
return letter.toUpperCase();
});
},
/**
* 任意の文字列をキャメルケースに変換(汎用版)
* @param {string} str - 変換対象の文字列
* @returns {string} キャメルケースに変換された文字列
*/
toCamelCase: function(str) {
if (!str || typeof str !== 'string') return '';
const words = str.match(/[A-Z]{2,}(?![a-z])|[A-Z]?[a-z]+|[A-Z]|[0-9]+/g);
if (!words) return str;
return words.map((word, index) => {
if (index === 0) {
return word.toLowerCase();
}
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}).join('');
},
/**
* オブジェクトのキーをすべてキャメルケースに変換
* @param {Object} obj - 変換対象のオブジェクト
* @returns {Object} キーがキャメルケースに変換された新しいオブジェクト
*/
objectKeysToCamel: function(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (Array.isArray(obj)) {
return obj.map(item => this.objectKeysToCamel(item));
}
return Object.keys(obj).reduce((acc, key) => {
const camelKey = this.toCamelCase(key);
acc[camelKey] = this.objectKeysToCamel(obj[key]);
return acc;
}, {});
}
};
// 使用例
console.log(CaseConverter.snakeToCamel('user_name')); // 'userName'
console.log(CaseConverter.pascalToCamel('UserName')); // 'userName'
console.log(CaseConverter.kebabToCamel('user-name')); // 'userName'
console.log(CaseConverter.toCamelCase('user_name')); // 'userName'
console.log(CaseConverter.toCamelCase('UserName')); // 'userName'
console.log(CaseConverter.toCamelCase('user-name')); // 'userName'
// オブジェクトの変換例
const data = { user_name: 'Taro', address_info: { zip_code: '123' } };
console.log(CaseConverter.objectKeysToCamel(data));
// { userName: 'Taro', addressInfo: { zipCode: '123' } }JavaScriptオブジェクト化するメリット
関数をオブジェクト(CaseConverter)にまとめることで、以下のような利点があります。
- 名前空間の汚染を防ぐ:グローバル変数と競合しにくくなります。
- コードの見通しが良くなる:
CaseConverter.snakeToCamel()と書くことで、文字列のケース変換であることが明確になります。 - 保守性が向上する:関連する関数が一箇所に集約されるため、修正や追加が容易になります。
- thisによる相互参照:
objectKeysToCamel内でthis.toCamelCaseを呼び出すことで、同じオブジェクト内の他のメソッドを利用できます。
まとめ
キャメルケースへの変換は、データの受け渡しやコードの統一性を保つ上で非常に重要な処理です。今回紹介したように、スネークケース・パスカルケース・ケバブケースという異なる形式から、一貫したキャメルケースへの変換を実現できます。
特に重要なのは、「入力値の検証を必ず行う」こと、「文字列は変更不可であることを理解する」こと、そして「連続した大文字や日本語などの特殊なケースへの配慮」の3点です。これらを押さえておけば、実務で発生する様々なケースに対応できるようになります。
まずは基本形のsnakeToCamelやpascalToCamelから使い始め、必要に応じて汎用的なtoCamelCaseやオブジェクト一括変換に拡張していくことをお勧めします。チームで共有するユーティリティとして整備することで、コードの一貫性と生産性が大幅に向上するはずです。
