Skip to content

Instantly share code, notes, and snippets.

@AndrewDongminYoo
Created October 17, 2024 04:44
Show Gist options
  • Select an option

  • Save AndrewDongminYoo/759c2665cf25751884bea643616780e3 to your computer and use it in GitHub Desktop.

Select an option

Save AndrewDongminYoo/759c2665cf25751884bea643616780e3 to your computer and use it in GitHub Desktop.
Dart extension methods for applying camelCase and snake_case to strings
extension StringExtensions on String {
/// ๋ฌธ์ž์—ด์„ ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋กœ ๋ณ€ํ™˜ํ•˜๋Š” `toSnakeCase()` ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ [String] ํด๋ž˜์Šค๋ฅผ ํ™•์žฅ
///
/// ์Šค๋„ค์ดํฌ ์ผ€์ด์Šค๋Š” ๋‹จ์–ด๋Š” ๋ฐ‘์ค„(`_`)๋กœ ๊ตฌ๋ถ„ํ•˜๊ณ  ๋ชจ๋“  ๋ฌธ์ž๋Š” ์†Œ๋ฌธ์ž๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ช…๋ช… ๊ทœ์น™์ž…๋‹ˆ๋‹ค.
/// ์ด ๋ฉ”์„œ๋“œ๋Š” ๋ฌธ์ž์—ด์˜ ๋ฌธ์ž๋ฅผ ๋ฐ˜๋ณตํ•˜์—ฌ ๋ฌธ์ž์—ด์˜ ์‹œ์ž‘ ๋ถ€๋ถ„์— ์—†๋Š” ๋Œ€๋ฌธ์ž ์•ž์— ๋ฐ‘์ค„์„ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ชจ๋“  ๋ฌธ์ž๋ฅผ ์†Œ๋ฌธ์ž๋กœ ๋ณ€ํ™˜
///
/// Example:
/// ```dart
/// 'HelloWorld'.toSnakeCase(); // 'hello_world'
/// 'XMLHTTPRequest'.toSnakeCase(); // 'xmlhttp_request'
/// ```
String toSnakeCase() {
// ๋ณ€ํ™˜๋œ ๋ฌธ์ž์—ด์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ๋นˆ StringBuffer
final buffer = StringBuffer();
// ์ด์ „ ๋ฌธ์ž๊ฐ€ ๋ฐ‘์ค„์ด์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด ๋ถ€์šธ ๋ณ€์ˆ˜
var previousUnderscore = false;
// for ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅ ๋ฌธ์ž์—ด์˜ ๊ฐ ๋ฌธ์ž๋ฅผ ๋ฐ˜๋ณต
for (var i = 0; i < length; i++) {
// ๊ฐ ๋ฌธ์ž์— ๋Œ€ํ•ด ํ•ด๋‹น ๋ฌธ์ž๊ฐ€ ๊ณต๋ฐฑ(' ')์ธ์ง€ ํ•˜์ดํ”ˆ('-')์ธ์ง€ ํ™•์ธ
if (this[i] == ' ' || this[i] == '-') {
// ์ด์ „ ๋ฌธ์ž๊ฐ€ ๋ฐ‘์ค„์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋ฒ„ํผ์— ๋ฐ‘์ค„('_')์„ ์ถ”๊ฐ€ํ•˜๊ณ  previousUnderscore๋ฅผ true๋กœ ์„ค์ •
if (!previousUnderscore) {
buffer.write('_');
previousUnderscore = true;
}
// ๋‘ ์กฐ๊ฑด ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์ฐธ์ด๋ฉด ๋ฒ„ํผ์— ๋ฐ‘์ค„('_')์„ ์ถ”๊ฐ€
// - ๋ฌธ์ž๊ฐ€ ๋Œ€๋ฌธ์ž์ธ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž์ธ์ง€ ๋˜๋Š” ์ด์ „ ๋ฌธ์ž๊ฐ€ ๋ฐ‘์ค„์ด์—ˆ๋Š”์ง€
// - ์ด์ „ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž์ธ์ง€ ๋˜๋Š” ๋‹ค์Œ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž์ธ์ง€
} else if (this[i].toUpperCase() == this[i] &&
this[i].toLowerCase() != this[i]) {
if (i != 0 && !previousUnderscore) {
// ํ˜„์žฌ ๋ฌธ์ž๊ฐ€ ๋Œ€๋ฌธ์ž์ด๊ณ  ์ด์ „ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž์ด๊ฑฐ๋‚˜ ๋‹ค์Œ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž์ธ ๊ฒฝ์šฐ
if (this[i - 1].toLowerCase() == this[i - 1] ||
(i + 1 < length && this[i + 1].toLowerCase() == this[i + 1])) {
buffer.write('_');
}
}
// ๋ฐ‘์ค„ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ฒ˜๋ฆฌํ•œ ํ›„ ํ˜„์žฌ ๋ฌธ์ž์˜ ์†Œ๋ฌธ์ž ๋ฒ„์ „์„ ๋ฒ„ํผ์— ์ถ”๊ฐ€ํ•˜๊ณ  previousUnderscore๋ฅผ false๋กœ ์„ค์ •
buffer.write(this[i].toLowerCase());
previousUnderscore = false;
// ๋ฌธ์ž๊ฐ€ ๊ณต๋ฐฑ, ํ•˜์ดํ”ˆ, ๋Œ€๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ ๋‹จ์ˆœํžˆ ๋ฒ„ํผ์— ํ•ด๋‹น ๋ฌธ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  previousUnderscore๋ฅผ false๋กœ ์„ค์ •
} else {
buffer.write(this[i]);
previousUnderscore = false;
}
}
// ๋ชจ๋“  ๋ฌธ์ž๋ฅผ ๋ฐ˜๋ณตํ•œ ํ›„ toString() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒ„ํผ๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ฐ˜ํ™˜
return buffer.toString();
}
/// ๋ฌธ์ž์—ด์„ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ camelCase ํ˜•์‹(์ฒซ ๋‹จ์–ด๋Š” ์†Œ๋ฌธ์ž๋กœ, ๊ทธ ์ดํ›„์˜ ๊ฐ ๋‹จ์–ด๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ช…๋ช… ๊ทœ์น™)์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
///
/// ์ด ๋ฉ”์„œ๋“œ๋Š” ๋‹จ์–ด๊ฐ€ ์•„๋‹Œ ๋ฌธ์ž(์˜ˆ: ๊ณต๋ฐฑ, ๊ตฌ๋‘์  ๋“ฑ)์™€ ์ผ์น˜ํ•˜๋Š” ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅ ๋ฌธ์ž์—ด์„ ๋‹จ์–ด ๋ฐฐ์—ด๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
/// ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹จ์–ด๋ฅผ ๋ฐ˜๋ณตํ•˜์—ฌ ๊ฐ ๋‹จ์–ด์˜ ์ฒซ ๊ธ€์ž๋Š” ๋Œ€๋ฌธ์ž๋กœ, ๋‚˜๋จธ์ง€๋Š” ์†Œ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
/// ๊ฒฐ๊ณผ ๋ฌธ์ž์—ด์€ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž๋กœ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
///
/// Example:
///
/// ```dart
/// 'hello_world'.toCamelCase(); // 'helloWorld'
/// 'XML_HTTP_REQUEST'.toCamelCase(); // 'xmlHttpRequest'
/// ```
String toCamelCase() {
// ๋จผ์ € ๋‹จ์–ด๊ฐ€ ์•„๋‹Œ ๋ชจ๋“  ๋ฌธ์ž(์˜ˆ: ๊ณต๋ฐฑ, ๊ตฌ๋‘์  ๋“ฑ)์™€ ์ผ์น˜ํ•˜๋Š” ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅ ๋ฌธ์ž์—ด์„ ๋‹จ์–ด ๋ฐฐ์—ด๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค.
final words = split(RegExp(r'[!@#<>?":`~;[\]\\|=+)(*&^%-\s_]+'));
// ์ž…๋ ฅ ๋ฌธ์ž์—ด์ด ๋น„์–ด ์žˆ๊ฑฐ๋‚˜ ๊ฒฐ๊ณผ ๋‹จ์–ด ๋ฐฐ์—ด์ด ๋น„์–ด ์žˆ์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
if (isEmpty || words.isEmpty) {
return '';
}
// ์ฒ˜๋ฆฌํ•  ๋‹จ์–ด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ƒˆ StringBuffer ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋Œ€/์†Œ๋ฌธ์ž ๋Œ€/์†Œ๋ฌธ์ž ๋ฌธ์ž์—ด์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
final buffer = StringBuffer();
// ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋ฐฐ์—ด์˜ ๊ฐ ๋‹จ์–ด๋ฅผ ๋ฐ˜๋ณตํ•ฉ๋‹ˆ๋‹ค:
for (final word in words) {
// ๋‹จ์–ด๊ฐ€ ๋น„์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๋‹จ์–ด์˜ ์ฒซ ๊ธ€์ž๋ฅผ ๋Œ€๋ฌธ์ž๋กœ ํ•˜๊ณ  StringBuffer์— ์ถ”๊ฐ€ํ•œ ๋‹ค์Œ ๋‚˜๋จธ์ง€ ๋‹จ์–ด๋Š” ์†Œ๋ฌธ์ž๋กœ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
if (word.isNotEmpty) {
buffer.write(word[0].toUpperCase() + word.substring(1).toLowerCase());
}
}
// ๋ชจ๋“  ๋‹จ์–ด๋ฅผ ์ฒ˜๋ฆฌํ•œ ํ›„ StringBuffer๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ถœ๋ ฅ ๋ณ€์ˆ˜์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
final output = buffer.toString();
// ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž๊ฐ€ ์†Œ๋ฌธ์ž๋กœ ๋œ ์ƒˆ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜๊ณ  ๋‚˜๋จธ์ง€ ์ถœ๋ ฅ ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
return output[0].toLowerCase() + output.substring(1);
}
}
// ๐Ÿ“ฆ Package imports:
import 'package:flutter_test/flutter_test.dart';
// ๐ŸŒŽ Project imports:
import 'case_converter.dart';
void main() {
group('StringExtensions', () {
group('toSnakeCase', () {
test('converts camelCase to snake_case', () {
expect('camelCase'.toSnakeCase(), 'camel_case');
expect('snakeCase'.toSnakeCase(), 'snake_case');
expect('PascalCase'.toSnakeCase(), 'pascal_case');
});
test('handles leading and trailing uppercase characters', () {
expect('LeadingUppercase'.toSnakeCase(), 'leading_uppercase');
expect('TrailingUppercase'.toSnakeCase(), 'trailing_uppercase');
});
test('handles consecutive uppercase characters', () {
expect('HTTPRequest'.toSnakeCase(), 'http_request');
expect('XMLHTTPRequest'.toSnakeCase(), 'xmlhttp_request');
});
test('handles empty string', () {
expect(''.toSnakeCase(), '');
});
test('handles string with only lowercase characters', () {
expect('lowercase'.toSnakeCase(), 'lowercase');
});
test('handles string with only uppercase characters', () {
expect('UPPERCASE'.toSnakeCase(), 'uppercase');
});
test('handles string with Title Case', () {
expect('Awesome Title Case'.toSnakeCase(), 'awesome_title_case');
expect('MixedCaseString'.toSnakeCase(), 'mixed_case_string');
expect('mixedCaseString'.toSnakeCase(), 'mixed_case_string');
});
test('handles dash-case', () {
expect('dash-is-also-good'.toSnakeCase(), 'dash_is_also_good');
expect('string--with--dashes'.toSnakeCase(), 'string_with_dashes');
});
});
test('handles string with numbers', () {
expect('string123'.toSnakeCase(), 'string123');
expect('123string'.toSnakeCase(), '123string');
expect('string123String'.toSnakeCase(), 'string123_string');
});
test('handles string with special characters', () {
expect(r'string@#$%'.toSnakeCase(), r'string@#$%');
expect(r'@#$%string'.toSnakeCase(), r'@#$%string');
expect(r'string@#$%String'.toSnakeCase(), r'string@#$%_string');
});
test('handles string with consecutive spaces', () {
expect('string with spaces'.toSnakeCase(), 'string_with_spaces');
});
test('handles string with leading and trailing spaces', () {
expect(' leadingSpace '.toSnakeCase(), '_leading_space_');
expect(' leadingSpace'.toSnakeCase(), '_leading_space');
expect('trailingSpace '.toSnakeCase(), 'trailing_space_');
});
test('handles string with leading and trailing dashes', () {
expect('-leadingDash-'.toSnakeCase(), '_leading_dash_');
expect('-leadingDash'.toSnakeCase(), '_leading_dash');
expect('trailingDash-'.toSnakeCase(), 'trailing_dash_');
});
});
group('toCamelCase', () {
test('converts snake_case to camelCase', () {
expect('snake_case'.toCamelCase(), 'snakeCase');
expect(
'snake_case_with_multiple_words'.toCamelCase(),
'snakeCaseWithMultipleWords',
);
});
test('converts kebab-case to camelCase', () {
expect('kebab-case'.toCamelCase(), 'kebabCase');
expect(
'kebab-case-with-multiple-words'.toCamelCase(),
'kebabCaseWithMultipleWords',
);
});
test('converts space-separated words to camelCase', () {
expect('space separated words'.toCamelCase(), 'spaceSeparatedWords');
expect(
'multiple space separated words'.toCamelCase(),
'multipleSpaceSeparatedWords',
);
});
test('handles leading and trailing underscores', () {
expect('_leading_underscore'.toCamelCase(), 'leadingUnderscore');
expect('trailing_underscore_'.toCamelCase(), 'trailingUnderscore');
});
test('handles leading and trailing hyphens', () {
expect('-leading-hyphen'.toCamelCase(), 'leadingHyphen');
expect('trailing-hyphen-'.toCamelCase(), 'trailingHyphen');
});
test('handles leading and trailing spaces', () {
expect(' leading space'.toCamelCase(), 'leadingSpace');
expect('trailing space '.toCamelCase(), 'trailingSpace');
});
test('handles consecutive separators', () {
expect('snake__case'.toCamelCase(), 'snakeCase');
expect('kebab--case'.toCamelCase(), 'kebabCase');
expect('space separated words'.toCamelCase(), 'spaceSeparatedWords');
});
test('handles mixed separators', () {
expect('snake_kebab-case'.toCamelCase(), 'snakeKebabCase');
expect('kebab-snake_case'.toCamelCase(), 'kebabSnakeCase');
expect(
'space separated_kebab-case'.toCamelCase(),
'spaceSeparatedKebabCase',
);
});
test('handles empty string', () {
expect(''.toCamelCase(), '');
});
test('handles string with only uppercase characters', () {
expect('UPPERCASE'.toCamelCase(), 'uppercase');
});
test('handles string with only lowercase characters', () {
expect('lowercase'.toCamelCase(), 'lowercase');
});
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment