The basic idea is that by defining three functions--recognize
, dissect
, and combine
--conversion between any case type is possible.
recognize
is meant to recognize a string of a given case typedissect
is meant to break down a string into an array of lower case substrings according to the given case typecombine
is meant to take those substrings and join them back up into a string of the given case type
For example, let's say someone wanted to add a new case type say, hyphenated case "my-name-is-jonas"
. With my code, all they would have to do is define the three aforementioned methods using the addType
method, and every other case type would be able to be converted to and from hyphenated case.
While this utility is pretty cool, I wonder if the complexity this solution adds to the code is worth it. Also, please let me know of any other thoughts that you might have.
Here is my code, but I also made a jsFiddle
var CaseConverter = (function() {
// Utility functions
/**
* Can be used by map function to lowercase array of strings
*/
function toLowerCase(string) {
return string.toLowerCase();
}
// private
var caseMap = {}; // used to map methods for a case conversion
var listOfCases = []; // a list of the added case types
/**
* Creates a function to convert a cased to string to the given type.
* @param {string} methodName the name of the conversion method to add
* @param {function} recognize Takes a string and returns true
* if the string is of the given type.
* @param {function} dissect Takes a string in the given case and
* returns an array of lowercase substrings of the case blocks.
* For example: [my, name, is, jonas] for "myNameIsJonas"
* "MyNameIsJonas"
* "my_name_is_jonas"
* @param {function} combine Takes an array of case blocks and
* returns them combined into a string of the desired case.
* substrings.
*/
function addType(methodName, recognize, dissect, combine) {
if (caseMap[methodName] !== undefined) {
throw "A method " + methodName + " already exists";
} else {
caseMap[methodName] = {
methodName: methodName,
recognize: recognize,
dissect: dissect,
combine: combine
};
listOfCases.push(methodName);
}
}
// add all the types of cases
addType("toCamelCase", function(string) {
return /^([a-z]+)([A-Z][a-z]*)*$/.test(string);
}, function(string) {
return string.split(/(?=[A-Z])/).map(toLowerCase);
}, function(array) {
return array.reduce(function(combined, caseBlock) {
return combined + caseBlock.charAt(caseBlock).toUpperCase() + caseBlock.substring(1);
});
});
addType("toPascalCase", function(string) {
return /^([A-Z][a-z]*)+$/.test(string);
}, function(string) {
return string.split(/(?=[A-Z])/).map(toLowerCase);
}, function(array) {
return array.reduce(function(combined, caseBlock) {
return combined + caseBlock.charAt(caseBlock).toUpperCase() + caseBlock.substring(1);
}, "");
});
addType("toUnderscoreCase", function(string) {
return /^[a-z]+(_[a-z]+)*$/.test(string);
}, function(string) {
return string.toLowerCase().split("_");
}, function(array) {
return array.join("_");
});
// someone could add hyphenated case here
// build the module
function createConverterFunction(typeTo) {
return function(string) {
if (typeof string !== "string") throw string + " is not a string";
var typeFrom = listOfCases.find(function(type) {
return caseMap[type].recognize(string);
});
if (typeFrom === undefined) throw string + " is not a recognized case type"
if (typeFrom === typeTo) {
return string;
} else {
var to = caseMap[typeTo];
var from = caseMap[typeFrom];
return to.combine(from.dissect(string));
}
};
}
var CaseConverter = {};
listOfCases.forEach(function(methodName) {
CaseConverter[methodName] = createConverterFunction(methodName);
})
return CaseConverter
})();
function test(method, string, expected) {
if (CaseConverter[method](string) !== expected)
throw "Failded for: " + method + ", string=" + string + ", expected=" + expected + ", result=" + CaseConverter[method](string);
}
test("toCamelCase", "myNameIsJonas", "myNameIsJonas");
test("toCamelCase", "MyNameIsJonas", "myNameIsJonas");
test("toCamelCase", "my_name_is_jonas", "myNameIsJonas");
test("toPascalCase", "myNameIsJonas", "MyNameIsJonas");
test("toPascalCase", "MyNameIsJonas", "MyNameIsJonas");
test("toPascalCase", "my_name_is_jonas", "MyNameIsJonas");
test("toUnderscoreCase", "myNameIsJonas", "my_name_is_jonas");
test("toUnderscoreCase", "MyNameIsJonas", "my_name_is_jonas");
test("toUnderscoreCase", "my_name_is_jonas", "my_name_is_jonas");
test("toCamelCase", "a", "a");
test("toPascalCase", "a", "A");
test("toUnderscoreCase", "a", "a");
console.log("all tests passed");
This question was inspired by this one