Exact solution asked for:
function megasplit(toSplit, splitters) { var splitters = splitters.sorted(function(a,b) {return b.length-a.length}); // sort by length; put here for readability, trivial to separate rest of function into helper function if (!splitters.length) return toSplit; else { var token = splitters[0]; return toSplit .split(token) // split on token .map(function(segment) { // recurse on segments return megasplit(segment, splitters.slice(1)) }) .intersperse(token) // re-insert token .flatten() // rejoin segments .filter(Boolean); } }
Demo:
> megasplit( "Go ye away, I want some peace && quiet. & Thanks.", ["Go ", ",", "&&", "&", "."] ) ["Go ", "ye away", ",", " I want some peace ", "&", "&", " quiet", ".", " ", "&", " Thanks", "."]
Machinery (reusable!):
Array.prototype.copy = function() { return this.slice() } Array.prototype.sorted = function() { var copy = this.copy(); copy.sort.apply(copy, arguments); return copy; } Array.prototype.flatten = function() { return [].concat.apply([], this) } Array.prototype.mapFlatten = function() { return this.map.apply(this,arguments).flatten() } Array.prototype.intersperse = function(token) { // [1,2,3].intersperse('x') -> [1,'x',2,'x',3] return this.mapFlatten(function(x){return [token,x]}).slice(1) }
Notes:
- This required a decent amount of research to do elegantly:
- This was further complicated by the fact the specification required that tokens (though they were to be left in the string) should NOT be split (or else you'd get
"&", "&"). This made use of reduce impossible and necessitated recursion. - I also personally would not ignore empty strings with splits. I can understand not wanting to recursively split on the tokens, but I'd personally simplify the function and make the output act like a normal
.split and be like ["", "Go ", "ye away", ",", " I want some peace ", "&&", " quiet", ".", " ", "&", " Thanks", ".", ""] - I should point out that, if you are willing to relax your requirements a little, this goes from being a 15/20-liner to a 1/3-liner:
1-liner if one follows canonical splitting behavior:
Array.prototype.mapFlatten = function() { ... } function megasplit(toSplit, splitters) { return splitters.sorted(...).reduce(function(strings, token) { return strings.mapFlatten(function(s){return s.split(token)}); }, [toSplit]); }
3-liner, if the above was hard to read:
Array.prototype.mapFlatten = function() { ... } function megasplit(toSplit, splitters) { var strings = [toSplit]; splitters.sorted(...).forEach(function(token) { strings = strings.mapFlatten(function(s){return s.split(token)}); }); return strings; }
"ye "and"away"be split? Seems like that should just be one if I'm understanding." I want some peace "does not split on spaces.