See edited post below for updated problem
=========================================

I've solved a dom manipulation problem, but I am not convinced that it is the cleanest and clearest way to solve it.

old problem: convert the innerHTML from a set of elements into three spans and replace the innerHTML of each element by the newly converted spans.

For example:

```
<h1>This is awesome<h1>
<h1>
  <span class="color-1">This</span>
  <span class="color-2">is</span>
  <span class="color-3">awesome</span>
</h1>

```

I have used a lot of slicing and joining and a recursive function, but I think I might get the same results with less code, for example making use of more es6 array helper methods. Can you help me?

Requirements
------------

1. InnerHTML words should be counted and divided by three
2. In case there is a residual, the last span should contain the least number of words, but never have less words than 1 compared to the first (and 2nd) span.
3. So with 15 words, each span contains 5
4. With 17 words, the first 2 spans should contain 6 words, the last one 5
5. With only 2 words, the first and second span should contain each 1 word and the last span is empty.

```
const elements = Array.from(document.querySelectorAll("h1, h2 , h3, h4"));
convertElements(elements);

function convertElements(elements) {
  const textsInsideElements = elements.map(htmlElement => htmlElement.innerHTML);
  textsInsideElements.forEach((text, index) =>
                        convertToSeparateSpans(elements, text, index));
}

function convertToSeparateSpans(elements, string, index) {
  const wordsPerSpan = calculateWordsPerSpan(string, 3);
  const wordParts = pushWordSpansToSeparateArrays([], string, wordsPerSpan, -1);
  const joinedTextSpans = wordsToSpanElements(wordParts);
  elements[index].innerHTML = joinedTextSpans;
}

function calculateWordsPerSpan(string, totalSpans) {
  const totalLength =  string.split(' ').length;
  const residual = totalLength % totalSpans;
  const firstPart = residual === 0 ? 
                      totalLength / totalSpans : Math.ceil(totalLength / totalSpans);
  const middlePart = residual === 1 ? firstPart * 2 - 1 : firstPart * 2;
  const endPart = totalLength;
  return [].concat(firstPart).concat(middlePart).concat(endPart);
}

function pushWordSpansToSeparateArrays(result, string, stringPartials, index) {
  const firstIteration = index === -1;
  const indexOutOfBounds = stringPartials[index + 1] === undefined;
  const stringToArr = string.split(' ');
  const firstString = stringToArr.slice(0, stringPartials[index + 1]).join(' ');
  const subsequentString = stringToArr
                             .slice(stringPartials[index], stringPartials[index + 1])
                             .join(' ');
  if(firstIteration) {
    result.push(firstString);
    pushWordSpansToSeparateArrays(result, string, stringPartials, index + 1)
    return result;
  } else if (!firstIteration && !indexOutOfBounds) {
    result.push(subsequentString);
    pushWordSpansToSeparateArrays(result, string, stringPartials, index + 1);   
  }
}

function wordsToSpanElements(wordParts) {
  const spanArray = wordParts.map((part, index) => 
  `<span class="color-${index+1}">${part}</span>`);
  return spanArray.join(' ');
}

```

EDIT
----

With some help from @Nathan Stockton, I finally formatted my code with this:

```
    function pushWordSpansToSeparateArrays(string) {
      const splitString = string.trim().split(" ");
      const hasResidual1 = splitString.length%3 === 1;
      const firstThird = Math.ceil(splitString.length/3);
      let secondThird = hasResidual1 ? firstThird * 2 - 1  : firstThird * 2;
      return []
        .concat(splitString.slice(0, firstThird).join(' '))
        .concat(splitString.slice(firstThird, secondThird).join(' '))
        .concat(splitString.slice(secondThird, splitString.length).join(' '));
    }
    const test = "This is a long string";
    console.log(pushWordSpansToSeparateArrays(test))
    // output ["This is", "a long", "string"]

```

The problem with this approach is that it is not reusable for more than 3 parts.

new problem: Is there a way to make this function general to have it split words into any number of parts based on an argument, for example pushWordSpansToSeparateArrays(string, totalParts);