Get an array of strings separating a string on N character incrementally So I asked a question over here, and even though I placed it in the wrong place (it should have been here), Redu was nice enough to give me a nice answer to my question.

Here is the original question for reference:

given the following input:

"/bob/ross/is/awesome/squirrels"


I would like to end up with:

[
    "/bob/ross/is/awesome/squirrels", 
    "/bob/ross/is/awesome", 
    "/bob/ross/is", 
    "/bob/ross", 
    "/bob"
]


The answer that Redu was nice enough to post was as follows:

res = s =&gt; s.length ? [s].concat(res(s.match(/.*(?=\/.+$)/)[0])) : [];


However he also commented the following:


  I honestly wouldn't advise recursion in JS, however since you have asked for it, i have provided a recursive answer down below.


Which makes me wonder, is there a better way to do this without recursion, but just as succinctly?

My original non-recursive function is as follows:



getWords = function( string ){

    // Get an array of all words in the provided string separating on "/"
    let words = string.split("/");

    // Filter out empty strings from leading/trailing "/" characters
    words = words.filter( function(a){return a !== ""} );

    // Create an array to store results in
    let results = [];

    // Create an iteration for each word in the array
    for (var i=0, j=words.length; i&lt;j; i++){

        // Create a string to concatenate to
        let result = "";

        // Loop over each word in the array minus the current iteration of "i"
        for (var k=0, l=words.length - i; k&lt;l; k++){

            // Contatenate using the special character plus the current word
            result += "/" + words[k];
        }

        // Push the resulting string to the results array
        results.push(result);
    }
    
    // Set the results to display
    resultsDisplay.innerHTML = results.toString().replace(/,/g, " -- ");

    // Return the results array
    return results;
}

input = document.getElementsByTagName("input")[0];
resultsDisplay = document.getElementsByClassName("results")[0];
input.addEventListener("input", function(){
getWords(input.value)
});
getWords(input.value);
&lt;input value="bob/ross/is/awesome/squirrels" /&gt;
&lt;p class="results"&gt;&lt;/p&gt;




This code is, admittedly, awful, hence my original post.
Which makes me wonder, is there a better way to do this without
  recursion, but just as succinctly?


Recursion can be an issue if your input size is huge and you recurse often and your JavaScript engine can't perform tail call optimization. Due to limited call stack size, you might then encounter a Maximum call stack size exceeded error. You might also want to avoid recursion if performance is a concern, as the function call overhead is not marginal in JavaScript.

So, how do you eliminate the recursion?

First of all, in order to gain a clear understanding of Redu's minified one-liner:

res = s =&gt; s.length ? [s].concat(res(s.match(/.*(?=\/.+$)/)[0])) : [];


I recommend to rewrite it into a self-documenting but otherwise equivalent function:

function expand(path) {
  if (path.length == 0) return [];
  let result = [path];

  path = path.match(/.*(?=\/.+$)/)[0];
  result = result.concat(expand(path));

  return result;
}


Now, we can analyze how that function works:


It appends the current path to the result
It replaces the current path with the parent path
It recurses to find a solution for the parent path
It appends the complete path to the solution for the parent path


It is always possible to turn a recursive function into a non-recursive function. We just need to manually keep track of already found paths and the current parent path:



function expand(path) {  
  let result = [];
  
  while (path.length &gt; 0) {
    result.push(path);
    path = path.match(/.*(?=\/.+$)/)[0];
  }
  return result;
}

console.log(expand("/bob/ross/is/awesome/squirrels"));




This non-recursive solution works as-is for empty strings without having to check for this base case. 

We could even minify it again, which I don't recommend:

const res=(s,r=[s])=&gt;{while(s.length)r.push(s=s.match(/.*(?=\/.+$)/)[0]);return r};


In a practical setting, I would recommend a more generative and more robust approach which, in a first step, extracts all valid path segments from the input. In a second step, you would then build the cumulative paths from those segments.

Using Array.map():


function expand(path, prefix = '') {
  return path.match(/[^\/]+/g).map((segment) =&gt; 
    prefix += '/' + segment
  ).reverse();
}

console.log(expand("/bob/ross/is/awesome/squirrels"));




Using Array.reduce():



function expand(path, prefix = '') {
  return path.match(/[^\/]+/g).reduce((path, segment) =&gt; {
    prefix += '/' + segment;
    path.unshift(prefix);
    return path;
  }, []);
}

console.log(expand("/bob/ross/is/awesome/squirrels"));