1

Essentially I want to port the solution for: Python spliting a list based on a delimiter word to JavaScript.

Given: var example = ['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'];

If a delimiter of WORD is provided I would like to generate:

var result = [['A'], ['WORD','B','C'],['WORD','D']];

Is there an alternative to looping through the list to do this?

Community
  • 1
  • 1
Hendrick Azen
  • 41
  • 3
  • 7

2 Answers2

1

this should do what you ask

var tree = function (seq, breaks) { 
    var res = [], n;
    for(var i = 0; i < seq.length; ) {
        if(breaks.indexOf(seq[i]) != -1) {
            for(var j = i+1; j < seq.length; j++) {
                if(breaks.indexOf(seq[j]) != -1) {
                    break;
                }
            }
            n = j;
            var branch = tree(seq.slice(i+1, n), breaks);
            branch.unshift(seq[i]);
            res.push(branch);
            i+=branch.length;
        } else {
            res.push([seq[i]]);
            i++;
        }
    }
    return res;
}

use

tree(['A', 'WORD', 'B' , 'C' , 'WORD' , 'D'], ['WORD'])
Simone Sanfratello
  • 1,520
  • 1
  • 10
  • 21
  • Can you verify that this algorithm will be amenable to tail-recursion optimization in supporting ES6 engines, or can you make it so? Also, your solution puts the first "A" directly in the result, whereas the OP wanted it to be in an array my itself. –  May 14 '15 at 03:25
  • I don't know how to verify what you ask about ES6, if you can tell me how I will; I fixed the code for the second part of your comment – Simone Sanfratello May 14 '15 at 08:47
  • For it to be tail-recursion, the recursive call needs to be the last thing in the execution flow, basically. –  May 14 '15 at 15:33
1

The best approach here is to first write down the algorithm, without getting into specific code. Sometimes this is called pseudo-code. Have you tried writing some? Here's an example:

  1. Start off with an empty result of the form [[]]. The inner array we will call subarray.

  2. Look at the next word in the input. If it's 'WORD', then add a new subarray to the result and make it the current subarray.

  3. Add the word to the current subarray.

  4. Repeat until input is empty.

This type of algorithm, where we are looping over an array, and building up some kind of result, is exactly what reduce was designed for. We can transform the pseudo-code above almost directly into JS as follows:

function split(array) {
  var subarray = [];                // subarray we are adding elts to

  return array.reduce(              // loop over the array
    function(result, elt) {         // and for each element
      if (elt === 'WORD')           // if it is word, then...
        result.push(subarray = []); // start a new subarray and add to result
      subarray.push(elt);           // add the current element to the subarray
      return result;                // and return the updated result
    }, 
    [subarray]);                    // start off with an array with a single subarray
}

Using generators

If you are working in an ES6 environment, you could use ES6 generators:

function* group(array) {
  var subarray = [];

  for (var elt of array) {
    if (elt === 'WORD') {
        yield subarray; 
        subarray = []; 
    }
    subarray.push(elt);
  }

  yield subarray;
}

Here, array can actually be any iterable, since we are using for..of to get its values.

Now you can print out your subarrays by

for (grp of group(example)) console.log(grp);

Or create an array of the groups:

Array.from(group(examples))

Is there an alternative to looping through the list to do this?

Someone is going to have to loop, be it you or some library routine. In the first case, it is reduce doing the looping; in the ES6 code, it is the generator.