JavaScript path parsing 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 &gt; 0) {
      parts.push(currentPart);
      currentPart = "";
    }
  }

  for (var i = 0; i &lt; 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 === "\'" &amp;&amp; insideIndexer) {
        terminatorExpected = ']';
      } else if (terminatorExpected === "\'" &amp;&amp; !insideIndexer) {
        flushCurrentPart();

        terminatorExpected = null;
      } else {
        currentPart += currentChar;
      }
      break;
    case "\"":
      if (!terminatorExpected || terminatorExpected === "]") {
        terminatorExpected = "\"";
      } else if (terminatorExpected === "\"" &amp;&amp; insideIndexer) {
        terminatorExpected = ']';
      } else if (terminatorExpected === "\"" &amp;&amp; !insideIndexer) {
        flushCurrentPart();

        terminatorExpected = null;
      } else {
        currentPart += currentChar;
      }
      break;
    default:
      currentPart += currentChar;
    } // switch (currentChar)
  } // for

  flushCurrentPart();

  return parts;
}
It would be a good idea to reimplement the whole thing using a regular expression.  Some uninitiated programmers despise or fear regular expressions, but in this case, avoiding them just means that you are giving up a language feature that is designed to solve just this kind of problem, forcing you to reinvent the wheel.

function parseJavaScriptPathIntoKeyNames(path) {
    /**
     * Repeatedly capture either:
     * - a bracketed expression, discarding optional matching quotes inside, or
     * - an unbracketed expression, delimited by a dot or a bracket.
     */
    var re = /\[("|'|)(.*?)\1\]|([^.\[\]]+)/g;

    var elements = [];
    var result;
    while ((result = re.exec(path)) !== null) {
        elements.push(result[2] || result[3]);
    }
    return elements;
}


I can't say for sure that this version behaves exactly the same as the original, especially in the face of illegal input.  However, the reduction in code makes it a much more worthwhile approach, and you can work out the details.