# PE # 34 - "Digit Factorials" with no loops

I posted a similar question “Curious Numbers” (HackerRank PE 34) recently (but in a different language/platform altogether). I'm starting to learn JavaScript and decided to take the challenge again and adapt it to this language's features and quirks.

While I could have written many of the functions below using for-loops, I decided instead to focus on getting better with the FP aspects of JS, in particular map, reduce, filter and apply.

I think it is pretty fast, with the longest of the 5 challenges on HackerRank executing in 2.28s for a calculation in the neighborhood of $10^5$, but I'm certainly open to ways to make it faster, cleaner, or better in any other way.

$19!$ is a curious number, as $1!+9!=1+362880=362881$ is divisible by $19$.

Find the sum of all numbers below $N$ which divide the sum of the factorial of their digits. Note: as $1!,2!,\cdots,9!$ are not sums, so they are not included.

Input Format: Input contains an integer $N$

Output Format: Print the answer corresponding to the test case.

Constraints: $10^1 \le N \le 10^5$

Sample Input

20


Sample Output

19

// HackerRank Project Euler #34: Digit factorials
// https://www.hackerrank.com/contests/projecteuler/challenges/euler034

function isStrictInt(input) {
// Confirms whether input is 1) of number type, 2) not equal to the NaN constant, and 3) can be parsed to an integer.
// Reference: http://stackoverflow.com/a/29658971/3626537
return typeof input === "number"
&& !isNaN(input)
&& parseInt(input) === input;
}

function arrayOfNCopies(value, N) {
// Makes an array of N copies of value.
if (!isStrictInt(N)) {
return NaN;
}
else if (N === 0) {
return [];
}
return Array(Math.abs(N) + 1).join(value).split("");
}

function arrayOfNConsecutiveInts(N) {
// Makes an array of consecutive integers from 1 to N.
// Ex: arrayOfNConsecutiveInts(5) => [1,2,3,4,5]
if (!isStrictInt(N)) {
return NaN;
}
else if (N === 0) {
return [];
}
return Array
// makes array of N size and assigns undefined to each index (to avoid nulls)
.apply(null, Array(N))
.map(function (a, b) { return b + 1; });
}

function powerOf(base, exponent) {
// Calculates an exponential (i.e. B^X or "B to the power of X").
// Ex: powerOf(2,3) => 2 * 2 * 2 => 8
if (!isStrictInt(base) || !isStrictInt(exponent)) {
return NaN;
}
else if (exponent === 0) {
return 1;
}
else if (base === 0) {
return 0;
}
else {
var result = arrayOfNCopies(base, exponent).reduce(function(a, b) { return a * b; });
if (exponent < 0) {
return 1 / result;
}
return result;
}
}

function factorial(N) {
// Calculates N! (i.e. N factorial).
// Ex: factorial(4) => 4 * 3 * 2 * 1 => 24
if (!isStrictInt(N)) {
return NaN;
}
// Negative numbers' factorials are mathematically undefined:
else if (N < 0) {
return NaN;
}
else if (N === 0) {
return 1;
}
return arrayOfNConsecutiveInts(N).reduce(function (a, b) {
return a * b;
});
}

function explodeIntToDigits(N) {
// Given a number N, decompose it into an array of its digits.
// Ex: explodeIntToDigits(1234) => [1, 2, 3, 4]
if (!isStrictInt(N)) {
return NaN;
}
return N.toString(10).split("").map(function (t) {
return parseInt(t);
});
}

function sumOfFactorialOfDigits(N) {
// Given a number N, returns the sum of the factorials of each digit of N.
// Ex: sumOfFactorialOfDigits(35) => 3! + 5! => 6 + 120 => 126
if (!isStrictInt(N)) {
return NaN;
}
return explodeIntToDigits(N).map(function (t) {
return factorial(t);
}).reduce(function (a, b) {
return a + b;
});
}

function isCuriousNumber(N) {
// A 'Curious Number' is a number where the sum of the factorial of each of its digits is evenly divisible by the number itself.
// Ex: isCuriousNumber(19) => true, because: 1! + 9! = 1 + 362880 = 362881, and: 362881 % 19 = 0
if (!isStrictInt(N)) {
return NaN;
}
else if (sumOfFactorialOfDigits(N) % N !== 0) {
return false;
}
return true;
}

function sumAllCuriousNumbersUpTo(N) {
// Given a number N up to 10^5, return the sum of a list of all 'Curious Numbers' 10 to N inclusive.
// This is as per constraint: 10 ≤ N ≤ 10^5
if (!isStrictInt(N)) {
return NaN;
}
return arrayOfNConsecutiveInts(N).filter(function (t) {
return t >= 10 && isCuriousNumber(t);
}).reduce(function (a, b) {
return a + b;
}, 0);
}


## Built-ins

Your powerOf function is mimicking Math.pow exactly. Use the built-in instead.

This also means you won't need arrayOfNCopies now.

## Consistent return values

You've done a good job doing type checking so your functions don't receive invalid inputs. But, sometimes your return values don't make sense (I only found one case).

Here: your arrayOfNConsecutiveInts returns NaN if N isn't a valid number, but an entirely different type - an array - if it is valid.

It's best to be consistent with your return types. Here, it might be best to throw an error.

## Refactor array summing

This construct

.reduce(function (a, b) {
return a + b;
});


has a few flaws, in my opinion:

• It's not very understandable at first glace (at least in my opinion, but I'm sure others will disagree).
• You repeat it a lot.

Also, while I'm not that familiar with it, is it still good practice in FP when one of a method's inputs is the object itself?

Either way, this could refactored to a separate function:

function sumArray(arr) {
... type checking...
arr.reduce(function(a, b) {
return a + b;
});
}


Or, if you use ES6 in the future, it could look even cleaner:

let sumArray = (arr) => arr.reduce((a, b) => a + b);


## Nitpicks

• Always supply parseInt with its second radix parameter.
• This:

else if (sumOfFactorialOfDigits(N) % N !== 0) {
return false;
}
return true;


can be simplified to:

return sumOfFactorialOfDigits(N) % N === 0

• doesn't !(sumOfFactorialOfDigits(N) % N !== 0) == sumOfFactorialOfDigits(N) % N === 0? Also in your sumArray(arr) function. You're missing a closing ) on the reduce Mar 19, 2016 at 0:27
• @Downgoat Yup, good catches. I've fixed my answer. Mar 19, 2016 at 13:32

### Refactored & improved code

I have applied suggestions from @SirPython and some others and refactored it significantly.

• Eliminated powerOf function and its dependent arrayOfNCopies. After looking through the code I noticed I was not even using powerOf once, so I didn't need to use the built-in Math.pow after all.
• Made functions that would normally return an array to throw a TypeError if the parameter(s) supplied cannot be used as such.
• Extracted the repeated reduce logic to sumArray and miltiplyArray functions.
• Added radix parameter to parseInt calls.
• Made return of isCuriousNumber more concise.

// HackerRank Project Euler #34: Digit factorials
// https://www.hackerrank.com/contests/projecteuler/challenges/euler034

function isStrictInt(input) {
// Confirms whether input is 1) of number type, 2) not equal to the NaN constant, and 3) can be parsed to an integer.
// Reference: http://stackoverflow.com/a/29658971/3626537
return typeof input === "number"
&& !isNaN(input)
&& parseInt(input, 10) === input;
}

function arrayOfNConsecutiveInts(N) {
// Makes an array of consecutive integers from 1 to N.
// Ex: arrayOfNConsecutiveInts(5) => [1,2,3,4,5]
if (!isStrictInt(N)) {
throw new TypeError("cannot parse " + N + " into an array of consecutive integers.");
}
else if (N === 0) {
return [];
}
return Array
// makes array of N size and assigns undefined to each index (to avoid nulls)
.apply(null, Array(N))
.map(function (a, b) { return b + 1; });
}

function sumArray(arr) {
if (!arr.reduce) {
throw new TypeError("Invalid argument " + arr + " to sumArray().");
}
return arr.reduce(function (curr, next) {
return curr + next;
}, 0);
}
function multiplyArray(arr) {
if (!arr.reduce) {
throw new TypeError("Invalid argument " + arr + " to multiplyArray().");
}
return arr.reduce(function (curr, next) {
return curr * next;
}, 1);
}

function factorial(N) {
// Calculates N! (i.e. N factorial).
// Ex: factorial(4) => 4 * 3 * 2 * 1 => 24
if (!isStrictInt(N)) {
return NaN;
}
// Negative numbers' factorials are mathematically undefined:
else if (N < 0) {
return NaN;
}
else if (N === 0) {
return 1;
}
return multiplyArray(arrayOfNConsecutiveInts(N));
}

function explodeIntToDigits(N) {
// Given a number N, decompose it into an array of its digits.
// Ex: explodeIntToDigits(1234) => [1, 2, 3, 4]
if (!isStrictInt(N)) {
throw new TypeError("cannot parse " + N + " into an array of digits.");
}
return N.toString(10).split("").map(function (t) {
return parseInt(t, 10);
});
}

function sumOfFactorialOfDigits(N) {
// Given a number N, returns the sum of the factorials of each digit of N.
// Ex: sumOfFactorialOfDigits(35) => 3! + 5! => 6 + 120 => 126
if (!isStrictInt(N)) {
return NaN;
}
return sumArray(explodeIntToDigits(N).map(factorial));
}

function isCuriousNumber(N) {
// A 'Curious Number' is a number where the sum of the factorial of each of its digits is evenly divisible by the number itself.
// Ex: isCuriousNumber(19) => true, because: 1! + 9! = 1 + 362880 = 362881, and: 362881 % 19 = 0
if (!isStrictInt(N)) {
return NaN;
}
return !(sumOfFactorialOfDigits(N) % N !== 0);
}

function sumAllCuriousNumbersUpTo(N) {
// Given a number N up to 10^5, return the sum of a list of all 'Curious Numbers' 10 to N inclusive.
// This is as per constraint: 10 ≤ N ≤ 10^5
if (!isStrictInt(N)) {
return NaN;
}
return sumArray(arrayOfNConsecutiveInts(N).filter(function (t) {
return t >= 10 && isCuriousNumber(t);
}));
}