Advent of Code 2017 Day 6 (part 1) in Functional Programming (FP)
Explanation

Algorithm

First we have to break down witch steps we have to do:


split bank string to an array
change all string values to a number
reallocate


reallocationRoutine

The algorithm above would look like this in JavaScript:

const reallocationRoutine = pipe (
    split (' '),                    // split number by space
    map (Number),                   // cast String to a number
    reallocation ([]) (0)           // reallocate with initial values
)

reallocationRoutine ('0 2 7 0')     // will return 5


Pipe is a kind of function composition.

reallocation


  I used loops and mutate variables inside my functions


You can avoid muting loops and variables by writing recursive functions. The cool thing about recursive functions is that the logic can be broken down into small steps:


we need the first index of the biggest number
we need the biggest number itself
if our store (memoryBanks) includes the current state (input)


we return the current cycleCount

else we call reallocation again, but there for we need to:


update the memoryBanks with the current input
increment the cycleCount
and redistribute the input



So it can looks like:

const reallocation = memoryBanks =&gt; cycleCount =&gt; input =&gt; {
    const firstIndexOfMax = input.indexOf(Math.max(...input))
    const max = input[firstIndexOfMax]
    return contains(memoryBanks, input)
        ? cycleCount
        : reallocation
            ( memoryBanks.concat ([input]) )
            ( increment (cycleCount) )
            ( redistribute (input) (firstIndexOfMax) (max) )
}


I used the ternary operator for conditions. The reason why is much more than only because it is shorter.
A if should always have an else and the ternary operator forces you to.

redistribute

Instead of putting the entire logic in redistribute, I wrote a function calledloop that traverses an array for a certain number of steps (steps) and a function (fn) on the current index of the passed array (xs) for each step. So we can create a more generic solution - one of the main ideas of FP.

const loop = fn =&gt; xs =&gt; index =&gt; steps =&gt;
    steps &lt;= 0
        ? xs
        : loop
            ( fn )
            ( changeArrayValueOnIndex(index, fn(xs[index]), xs) )
            ( getNextIndex(xs, index) )
            ( steps - 1 )


redistribute is a special form of loop where we increment each index we reaches and modify the passed input xs so, that the start value gets set to 0

const redistribute = xs =&gt; index =&gt;
    loop (increment) (changeArrayValueOnIndex(index, 0, xs)) (getNextIndex(xs, index))


Working Example

Overall, the code is longer than the imperative solution, but only because I've encapsulated a lot of logic into its own function like increment and loop. However, these are now functions that we could reuse in our next project and, in addition, we can test everything much easier :)



// helper functions
const pipe = (...fns) =&gt; fns.reduce((f, g) =&gt; (...args) =&gt; g(f(...args)))

const map = fn =&gt; arr =&gt;
    arr.map(fn)
    
const split = delimiter =&gt; str =&gt;
    str.split(delimiter)
    
const arrayIsEqual = ys =&gt; xs =&gt;
    xs.length === ys.length
        ? xs.every((x, index ) =&gt; ys[index] === x)
        : false

const contains = (xss, ys) =&gt;
    xss
      .map(arrayIsEqual(ys))
      .some(bool =&gt; bool)
    
const getNextIndex = (arr, index) =&gt;
    arr[index + 1] != undefined ? index + 1 : 0
    
const increment = x =&gt;
    x + 1
    
const changeArrayValueOnIndex = (index, value, array) =&gt;
    [].concat(array.slice(0, index), value, array.slice(index + 1))
    
    
// functions for businesslogic    
const loop = fn =&gt; xs =&gt; index =&gt; steps =&gt;
    steps &lt;= 0
        ? xs
        : loop
            ( fn )
            ( changeArrayValueOnIndex(index, fn(xs[index]), xs) )
            ( getNextIndex(xs, index) )
            ( steps - 1 )

const redistribute = xs =&gt; index =&gt;
    loop (increment) (changeArrayValueOnIndex(index, 0, xs)) (getNextIndex(xs, index))

const reallocation = memoryBanks =&gt; cycleCount =&gt; input =&gt; {
  const firstIndexOfMax = input.indexOf(Math.max(...input))
  const max = input[firstIndexOfMax]
  return contains(memoryBanks, input)
    ? cycleCount
    : reallocation
        ( memoryBanks.concat([input]) )
        ( increment(cycleCount) )
        ( redistribute (input)(firstIndexOfMax)(max) )
}

const reallocationRoutine = pipe(
    split (' '),
    map (Number),
    reallocation ([]) (0)
)

console.log('0 2 7 0:', reallocationRoutine('0 2 7 0'))
console.log('2 8 8 5 4 2 3 1 5 5 1 2 15 13 5 14:', reallocationRoutine('2 8 8 5 4 2 3 1 5 5 1 2 15 13 5 14'))