'Output on Narcissistic Numbers keeps being undefined
I am trying to solve a kata from Codewars but I bumped into a wall. I'll leave the instructions down below and my code. If anyone can guide me through the answer or simply what is wrong with my code I will kindly appreciate it.
Instructions
A Narcissistic Number is a positive number which is the sum of its own digits, each raised to the power of the number of digits in a given base. In this Kata, we will restrict ourselves to decimal (base 10).
For example, take 153 (3 digits), which is narcisstic:
1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153
and 1652 (4 digits), which isn't:
1^4 + 6^4 + 5^4 + 2^4 = 1 + 1296 + 625 + 16 = 1938
My Code
function narcissistic(value) {
let sum = 0;
for(let i = 0; i < value.length; i++){
sum += Math.pow(value[i], 3);
if(sum == value){
return true;
}
return false;
}
}
Solution 1:[1]
I'd personally suggest:
// we accept a numeric representation, either 123 or "123":
const isNarcissistic = (number) => {
// here we set the passed-in number to a String,
// and then split that string to an Array of
// single-character number-strings;
// 123 -> "123" -> ["1","2","3"],
// "123" -> "123" -> ["1","2","3"]:
let numbers = number.toString().split(''),
// we find the length of the Array (the
// amount of characters in the original
// passed-in number to determine the power;
// 123 = length of 3:
power = numbers.length,
// we use Array.prototype.reduce() to
// calculate the sum of the values raised
// to the power we found earlier;
// acc: the accumulator,
// curr: the current array-element of the
// Array we're iterating over:
res = numbers.reduce((acc, curr) => {
// here we add the current-value of the
// accumulator to the result of raising
// the current-value (passed to parseInt()
// with a radix of 10) to the power we
// found earlier:
return acc + Math.pow(parseInt(curr, 10), power);
// setting the accumulator to an initial value
// of zero:
}, 0)
// here we return whether the final result is equal to
// the number passed in, whether as a number or
// as a String, using parseInt() with a radix of
// 10, to get a base-10 number:
return res === parseInt(number, 10);
}
// this is a useage demonstration; and largely irrelevant to the posted problem,
// however: we use document.querySelectorAll() to retrieve all elements in the
// document that match the supplied CSS selector, this returns a NodeList, which
// we then pass to NodeList.prototype.forEach():
document.querySelectorAll('li').forEach(
// here we use an Arrow function, to pass the current element-node of the
// NodeList over which we're iterating to the single-line function-body;
// in that function body we access the classList of the element,
// and use the toggle method passing in the name of the class to toggle,
// and call our function (isNarcissistic) to determine whether the
// textContent of the current element is narcissistic; if the function returns
// (Boolean) true the class-name is added (or kept), if the function returns
// (Boolean) false the class-name is not added (or it's removed):
(el) => el.classList.toggle('isNarcissistic', isNarcissistic(el.textContent))
)
*,
::before,
::after {
box-sizing: border-box;
font: normal 1rem / 1.5 sans-serif;
margin: 0;
padding: 0;
}
ul {
display: flex;
justify-content: space-around;
gap: 1em;
width: 90vw;
margin-inline: auto;
margin-block: 1em;
}
li {
list-style-type: none;
border: 2px solid transparent;
flex-grow: 1;
}
.isNarcissistic {
border-color: lime;
}
<ul>
<li>153</li>
<li>370</li>
<li>406</li>
<li>407</li>
<li>1634</li>
</ul>
The problems with your existing solution:
// you use the 'value' argument within your
// function without checking that it's a
// Number or String (if you're the only one
// using it, that's fine, but usually unsafe):
function narcissistic(value) {
let sum = 0;
// your for initialises i as zero, which is fine
// as it's an iterator, but you then test that
// i is less than the length of 'value', which
// if 'value' is a Number will evaluate to false
// because a Number has no 'length' property,
// if it evaluates to false the loop will never
// be entered (and the function will return false)
// if 'value' is a String, the loop will be entered:
for(let i = 0; i < value.length; i++){
// here we add the result of raising the
// character held at the 'ith' position
// of the String, and it will be raised
// to the power of 3 (regardless of the
// number of characters in the String, so
// any number of less, or more, than 3
// characters will not behave as expected:
sum += Math.pow(value[i], 3);
// here you use '==' which will return
// true for "123" == 123; the second
// argument will be coerced to the same
// type of variable as the first, whether
// from String to Number, or Number to String
// (this can be useful if you know what
// you're doing, but usually '===' is preferred
// since that checks that the two arguments are
// exactly equal, both are Numbers, or Strings,
// and that String or Number has the same value):
if(sum == value){
return true;
}
return false;
}
}
References:
Solution 2:[2]
There are 3 issues:
valueis allowed to be aNumberand not forced to be aStringwhich would cause.lengthand[i]to beundefined. Which prevents theforloop from even runningMath.powis always called with an exponent of3which is not dynamic based on the input as the spec requires- the return statement is inside the
forloop. This means it will return true/false on the first iteration
Fixing these 3 would look something like this (I did not test this code yet)
function narcissistic(value) {
// always treat it as text for .length and [index] accessors
// if the number started with a `0` it would have to be passed in as a string regardless as `Number` does not support that...
value = value.toString();
// sum is a number as we add to it
let sum = 0;
// since value is a string `.length` will give us the digits
const digits = value.length;
for(let i = 0; i < digits; i++) {
// Math.pow() supports number strings for the first argument
// note that floating point numbers will break this as `.` is invalid
sum += Math.pow(value[i], digits);
}
// == does a loose comparison so 1567 == '1567' is true
return sum == value;
}
Solution 3:[3]
You are doing it completely wrong. You cannot iterate over a number like that because it is not a string hence value[i] will always return undefined. Here is the correct way
/*Getting number of digits*/
function getlength(number) {
return number.toString().length;
}
function narcissistic(value) {
let power = getlength(value);
let sum = 0;
let temp_val= new Number(value); // Creating a new copy of the value at different location so they do'nt reference to same number
while(temp_val>0){ // Getting digits from right to left
let digit = temp_val%10;
sum += Math.pow(digit, power);
temp_val= Math.floor(temp_val/10); // Taking only integer values
}
if(sum == value){
return true;
}
return false;
}
let value = 153
console.log(narcissistic(value));
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 |
