简体   繁体   中英

Converting Javascript solution to Functional Programming Approach

I have (very) recently gotten quite interested in functional programming, and in particular, how to apply this to my work in JavaScript. After answering a question regarding regex use (link here ), I continued to develop the ideas a bit more with the intent of using this to compare with a functional programming approach.

The challenge is to write a simple input parser that takes a regex and some input and returns a matching array of objects (this is step 1 of the bigger solution, but I wanted to start simple). I have it working with the more traditional approach, but would like to do the equivalent with functional programming (I am using ramda.js, but open to any functional programming approach as long as it is in JavaScript).

Here's the working code:

var parseInput = function (re, input) {
  var results = [], result;
  while ((result = re.exec(input)) !== null) {
    results.push({
      startPos: result.index,
      endPos: re.lastIndex - 1,
      matchStr: result[1]
    })
  }
  return results;
};

var re = /<%([^%>]+)%>/g;
var input = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD";

var results = parseInput(re, input);
console.log(results);

The output I get looks like this:

[ { startPos: 2, endPos: 15, matchStr: 'test.child' },
  { startPos: 16, endPos: 23, matchStr: 'more' },
  { startPos: 31, endPos: 38, matchStr: 'name' },
  { startPos: 45, endPos: 51, matchStr: 'age' } ]

which is the structure and results I am looking for.

In particular, I have been experimenting with Ramda and the 'match()' function, but I can't see a clean way to get the array of objects I am looking for (short of running the match() to get an array of matches and then looking each one up in the original input which seems no less cumbersome than my current solution).

Guidance would be appreciated.

You're right that Ramda's match will not help you. It's designed for simpler uses. I don't see anything substantially better than your code, although I might factor it differently:

const execAll = R.curry((re, convert, input) => {
  let results = [], result;
  while ((result = re.exec(input))) {
    results.push(convert(result))
  }
  return results;
});

const parseInput = execAll(/<%([^%>]+)%>/g, match => ({
  startPos: match.index,
  endPos: match.index + match[0].length - 1,
  matchStr: match[1]
}));

const input = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD";

parseInput(input);

Obviously, this code is structured differently, breaking apart the looping of the regex exec calls from the formatting of the output. More subtly, though, it also does not rely on the global state of the regex, using only information from the returned match results for its output. That strikes me as important for functional programming.

The call to Ramda's curry is pure gravy. You could also write this as

const execAll = (re, convert) => (input) => { /* ... */ }

This is available on the Ramda REPL , if you're interested.

Do note that this is not much changed from your approach, though. I don't see a significantly different approach applicable across a range of regular expressions.

Just slightly altering your regex you might do as follows by using the String.prototype.match() method;

 var str = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD", rex = /[^<%]+(?=%>)/g, res = str.match(rex); console.log(res); 

Well in case you will have this strict conditional structure of regex applying all the time then you might consider a non regex functional code doing the same job in a much faster manner as follows;

 var str = "A <%test.child%><%more%> name: <%name%> age: <%age%> EOD", res = Array.prototype.reduce.call(str, function(r,c,i,s){ c === "%" && s[i-1] === "<" ? (r.select = true, r.push({startPos:i+1, endPos:undefined, matchStr: ""})) : c === ">" && s[i-1] === "%" ? (r.select = false, r[r.length-1].endPos = i-2) : r.select && c !== "%" && (r[r.length-1].matchStr.length ? r[r.length-1].matchStr += c : r[r.length-1].matchStr = c); return r; },[]); console.log(res); 

You will notice that the start and end positions differ from your example and that's just because they are giving the true start and end positions of the matched sub strings. You may easily alter the code to include the indices of <% and %> as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM