Stateless Maximum Beauty of String implementation Inspired by The Beauty and the Strings question, I tried to implement the "Maximum Beauty of String" in a stateless fashion using ES6. Unfortunately, I ran into two places where I had to assign variables.

The questions I have:


How can I remove the state in the reducer for mapLettersByCount?
How can one use generators without assigning it to a variable? (see points variable in the maxBeauty function) Is this even considered state in this situation?


Code:

const EMPTY_STRING = '';
const LETTERS_ONLY = /[a-z]/i;

function mapLettersByCount(sentence) {
  return sentence.split(EMPTY_STRING)
    .filter(letter =&gt; LETTERS_ONLY.test(letter))
    .map(letter =&gt; [letter.toUpperCase(), 1])
    .reduce((letterCount, [key, count]) =&gt; {
      letterCount[key] = (letterCount[key] || 0) + count;
      return letterCount;
    }, {});
}

function * downCounter(maxValue) {
  yield maxValue;
  yield * downCounter(maxValue &gt; 0 ? maxValue - 1 : 0);
}

export function maxBeauty(sentence = "") {
  const points = downCounter(26);
  return Object.values(mapLettersByCount(sentence))
    .sort().reverse()
    .reduce((score, count) =&gt; score + count * points.next().value, 0);
}


Test Cases:

import {maxBeauty} from './beauty';

describe('The maximum beauty', () =&gt; {
  it('should calculate for a word', () =&gt; {
    expect(maxBeauty('AbBCcC')).toBe(152);
  });

  it('should calculate ignoring spaces', () =&gt; {
    expect(maxBeauty('This is from Facebook Hacker Cup 2013.'))
      .toBe(551);
  });

  it('should calculate ignoring punctuation', () =&gt; {
    expect(maxBeauty('Ignore punctuation, please :)'))
      .toBe(491);
  });
});
How can I remove the state in the reducer for mapLettersByCount?


Return a new object on each iteration. Not optimal, but we're talking functional anyways. You can use object destructuring to do a shallow copy of the object. A more verbose version would be Object.assign. Then use computed keys to assign a dynamic key.

function mapLettersByCount(sentence) {
  return sentence.split(EMPTY_STRING)
                 .filter(letter =&gt; LETTERS_ONLY.test(letter))
                 .map(letter =&gt; [letter.toUpperCase(), 1])
                 .reduce((letterCount, [key, count]) =&gt; ({
                   ...letterCount,
                   [key]: (letterCount[key] || 0) + count
                 }), {});
}



  How can one use generators without assigning it to a variable? (see points variable in the maxBeauty function) Is this even considered state in this situation?


I believe that it is stateful since one call to points.next() does not return the same result as the next call. But I may be wrong.

One way you could do this is compute the points by subtracting the reducer's index from a constant 26. I believe it won't violate the rules of functional programming because


this 26 is a constant value
it's technically a closure scenario, similar to your other constants and
index is provided as an argument to the callback.


And it would look like this:

const POINTS = 26;

export function maxBeauty(sentence = "") {
  return Object.values(mapLettersByCount(sentence))
               .sort()
               .reverse()
               .reduce((score, count, index) =&gt; score + count * (POINTS - index), 0);
}