diff --git a/Conversions/CamelPascalToSnakeCase.js b/Conversions/CamelPascalToSnakeCase.js new file mode 100644 index 0000000000..e768a47f5c --- /dev/null +++ b/Conversions/CamelPascalToSnakeCase.js @@ -0,0 +1,44 @@ +/** + * Transforms a camelCase (or PascalCase) string to snake_case + * @param {string} inputStr camelCase or PascalCase string + * @returns { string } snake_case string + */ + +function camelPascalToSnakeCase(inputStr) { + if (typeof inputStr !== 'string') { + throw new Error(`Expected string as input, found ${typeof inputStr}`) + } + + let snakeStr = '' + let lastCharWasLower = false + + for (let index = 0; index < inputStr.length; index++) { + const char = inputStr[index] + + if ( + /[A-Z]/.test(char) && + (!lastCharWasLower || (index > 0 && /[a-z0-9]/.test(inputStr[index - 1]))) + ) { + if (index > 0 && inputStr[index - 1] !== '_') { + snakeStr += '_' + } + snakeStr += char.toLowerCase() + lastCharWasLower = false + } else if (/[a-z0-9]/.test(char)) { + snakeStr += char + lastCharWasLower = /[a-z]/.test(char) + } else { + snakeStr += '_' + lastCharWasLower = false + } + } + + // Remove leading underscore + if (snakeStr[0] === '_') { + snakeStr = snakeStr.substring(1) + } + + return snakeStr +} + +export { camelPascalToSnakeCase } diff --git a/Conversions/test/CamelPascalToSnakeCase.test.js b/Conversions/test/CamelPascalToSnakeCase.test.js new file mode 100644 index 0000000000..7c6a598082 --- /dev/null +++ b/Conversions/test/CamelPascalToSnakeCase.test.js @@ -0,0 +1,21 @@ +import { camelPascalToSnakeCase } from '../CamelPascalToSnakeCase' + +describe('camelToSnakeCase', () => { + it.each([ + ['someRandomString', 'some_random_string'], + ['SomeRandomStr#ng', 'some_random_str_ng'], + ['_SomeRandomStr#ng', 'some_random_str_ng'], + ['123someRandom123String123', '123some_random123_string123'], + ['123SomeRandom123String123', '123_some_random123_string123'], + ['123SomeRandom123String123', '123_some_random123_string123'], + ['', ''] + ])('converts %s to snake_case %s', (input, expected) => { + expect(camelPascalToSnakeCase(input)).toBe(expected) + }) + + it('throws an error when the input is not a string', () => { + expect(() => camelPascalToSnakeCase(123)).toThrow( + 'Expected string as input' + ) + }) +}) diff --git a/DIRECTORY.md b/DIRECTORY.md index 6c1fa7aeb8..71ac36fc5e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -41,6 +41,7 @@ * [Base64ToArrayBuffer](Conversions/Base64ToArrayBuffer.js) * [BinaryToDecimal](Conversions/BinaryToDecimal.js) * [BinaryToHex](Conversions/BinaryToHex.js) + * [CamelPascalToSnakeCase](Conversions/CamelPascalToSnakeCase.js) * [DateDayDifference](Conversions/DateDayDifference.js) * [DateToDay](Conversions/DateToDay.js) * [DecimalToBinary](Conversions/DecimalToBinary.js)