Breaking an integer up into chunks, functionally

Consider the problem of decomposing milliseconds into readable time units. Imagine you had a function that did that

> breakupMillis(100000000)
Array [ 0, 40, 46, 3, 1 ]

meaning that 100 million milliseconds is 1 day, 3 hours, 46 minutes, and 40 seconds, exactly.

  • Index inside map() function
  • How to map over arbitrary Iterables?
  • Function declarations cannot be nested within non-function blocks
  • How to update element inside List with ImmutableJS?
  • How come I can pass functions to a lifted R.divide?
  • Can I make this function defenition even shorter?
  • The function could be generalized by accepting an array of moduli, like this

    > breakup(100000000, [1000, 60, 60, 24]) 
    Array [ 0, 40, 46, 3, 1 ]
    

    That function could be used (hypothetically) for other things:

    > breakup(1000, [8, 8, 8]) 
    Array [ 0, 5, 7, 1 ]
    

    meaning that 1000 in decimal is 01750 in octal.

    Here is the function I wrote to do this:

    const breakup = (n, l) => l.map(p => 
        { const q = n % p; n = (n - q) / p; return q; }).concat(n);
    

    This function is fine, it’s even referentially transparent, but I have two, entirely esthetic, complaints.

    • the map. This feels like a job for reduce, though I don’t see how.
    • rewriting the variable n. I don’t like to use var at all; using a secret var makes it worse.

    My question is only about the second. How do I re-write the function so it uses no variable (that actually vary)? If the map disappears, I’ll take that as gravy.

  • Are 'currying' and 'composition' the same concept in Javascript?
  • Functional Javascript BaconJS, how can I push more values to an event stream?
  • Javascript equivalent to Clojure's “reductions” or python's itertools.accumulate
  • React - What's the great benefit of having immutable props?
  • JavaScript: Can this code be refactored to a more functional programming style?
  • Howto move from object based language to server side Node.js Javascript for big projects?
  • 3 Solutions collect form web for “Breaking an integer up into chunks, functionally”

    Here’s another way you can do it using a recursive procedure and a little helper quotrem – which given a numerator n, and a denominator d, returns [<quotient>, <remainder>]

    const quotrem = (n, d) => [n / d >> 0, n % d]
    
    const breakup = (n, [x,...xs]) => {
      if (x === undefined) {
        return [n]
      }
      else {
        let [q, r] = quotrem(n, x)
        return [r, ...breakup(q, xs)]
      }
    }
    
    console.log(breakup(1000, [8, 8, 8]))
    // [ 0, 5, 7, 1 ]
    
    console.log(breakup(100000000, [1000, 60, 60, 24]))
    // [ 0, 40, 46, 3, 1 ]

    This feels like a job for reduce, though I don’t see how.

    Everything that iterates an array can be done with reduce 🙂

    We need to pass two things through (for the accumulator): the number that we still have the break, and the list of results. We can use ES6 destructuring and an array as a tuple:

    function breakup(n, units) {
        const [n, res] = units.reduce(([n, res], u) => {
            const q = n % u;
            res.push((n-q) / u);
            return [q, res];
        }, [n, units]);
        return [n, ...res];
    }
    

    But that push is still ugly. Not only because it mutates (where we could have used concat as well), but what we really want is a function that abstracts this away. Unfortunately these don’t exists in JS – we’d be looking for a scan or mapping accumulation. We could write

    function breakup(n, units) {
        const [rest, res] = units.mapAccum((n, u) => {
            const q = n % u;
            return [q, (n-q) / u];
        }, [n, units]);
        return [...res, rest];
    }
    function breakup(n, units) {
        const mods = units.scan((n, u) => Math.floor(n/u), units);
        return mods.map((q, i) => i<units.length ? q % units[i] : q);
    }
    

    I’ll leave the (functional, efficient, readable, etc) implementation of those as an exercise to the reader.

    I would suggest this code:

    const breakup = (n, l) => 
        l.reduce( ([n, ...res], p) => ([(n - n % p) / p, n % p, ...res]), [n])
         .reverse();
    
    // Demo call
    console.log(breakup(100000000, [1000, 60, 60, 24]));