I was attempting to do some contribution to the underscore-contrib library, and I ended up writing a function that will parse a JavaScript path to later on expose through an API if the path exists or to get the value.
However, a reasonable concern around it is the complexity. (Discussion in the repository, if you're interested.)
I did review other approaches to the problem, but most of them seem to leave valid complex property names behind, or some even are tied to actually retrieving the value at that point.
Can this be made simpler?
// Will take a path like 'element[0][1].subElement["Hey!.What?"]["[hey]"]'
// and return ["element", "0", "1", "subElement", "Hey!.What?", "[hey]"]
function parseJavaScriptPathIntoKeyNames(javascriptPath) {
var parts = [];
var terminatorExpected = null;
var insideIndexer = false;
var currentPart = "";
function flushCurrentPart() {
if (currentPart.length > 0) {
parts.push(currentPart);
currentPart = "";
}
}
for (var i = 0; i < javascriptPath.length; i++) {
var currentChar = javascriptPath[i];
switch (currentChar) {
case "[":
if (!terminatorExpected) {
flushCurrentPart();
terminatorExpected = ']';
insideIndexer = true;
} else {
currentPart += currentChar;
}
break;
case "]":
if (terminatorExpected === "]") {
flushCurrentPart();
terminatorExpected = null;
insideIndexer = false;
} else {
currentPart += currentChar;
}
break;
case ".":
if (!terminatorExpected) {
flushCurrentPart();
} else {
currentPart += currentChar;
}
break;
case "\'":
if (!terminatorExpected || terminatorExpected === "]") {
terminatorExpected = "\'";
} else if (terminatorExpected === "\'" && insideIndexer) {
terminatorExpected = ']';
} else if (terminatorExpected === "\'" && !insideIndexer) {
flushCurrentPart();
terminatorExpected = null;
} else {
currentPart += currentChar;
}
break;
case "\"":
if (!terminatorExpected || terminatorExpected === "]") {
terminatorExpected = "\"";
} else if (terminatorExpected === "\"" && insideIndexer) {
terminatorExpected = ']';
} else if (terminatorExpected === "\"" && !insideIndexer) {
flushCurrentPart();
terminatorExpected = null;
} else {
currentPart += currentChar;
}
break;
default:
currentPart += currentChar;
} // switch (currentChar)
} // for
flushCurrentPart();
return parts;
}