Each digit differs from the next one by 1 - javascript

Script has to work this way:
isJumping(9) === 'JUMPING'
This is a one digit number
isJumping(79) === 'NOT JUMPING'
Neighboring digits do not differ by 1
isJumping(23454) === 'JUMPING'
Neighboring digits differ by 1
I have:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(str[i+1]) - Math.abs(str[i]) == 1){
return 'JUMPING';
}
}
return 'NOT JUMPING';
}
console.log(isJumping(345));
Help please, where is mistake?

Loop over the characters and early return with "NOT JUMPING" if the condition is violated & if the condition is never violated return "JUMPING".
function isJumping(num) {
const strNum = String(Math.abs(num));
for (let i = 0; i < strNum.length - 1; i++) {
if (Math.abs(strNum[i] - strNum[i + 1]) > 1) {
// replace `> 1` with `!== 1`, if diff 0 is not valid!
return "NOT JUMPING";
}
}
return "JUMPING";
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));

There are a couple of issues:
You're not handling single digits.
You're returning too early. You're returning the first time you see a difference of 1 between digits, but you don't know that subsequent differences will also be 1.
You're not checking the difference between the first and second digits, and you're going past the end of the string.
You're using Math.abs as a means of converting digits to numbers.
Instead (see comments):
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
// Convert to number, do the difference, then
// use Math.abs to make -1 into 1 if necessary
if (Math.abs(+str[i] - str[i-1]) !== 1) {
// Found a difference != 1, so we're not jumping
return "NOT JUMPING";
}
}
// Never found a difference != 1, so we're jumping
return "JUMPING";
}
console.log(isJumping(345)); // JUMPING
console.log(isJumping(9)); // JUMPING
console.log(isJumping(79)); // NOT JUMPING
console.log(isJumping(23454)); // JUMPING
In that, I use +str[i] to convert str[i] to number and implicitly convert str[i-1] to number via the - operator, but there are lots of ways to convert strings to numbers (I list them here), pick the one that makes sense for your use case.
You might also need to allow for negative numbers (isJumping(-23)).

A clumsy way would be if (Math.abs(Math.abs(str[i+1]) - Math.abs(str[i])) == 1). Right now you are using Math.abs() to convert digits to numbers. Also, indexing is off, you start from 1, which is good, but then you should compare [i] and [i-1]. And the usual mismatch: you can say "JUMPING", only at the end. So you should check for !==1, and return "NOT JUMPING" inside the loop, and "JUMPING" after. That would handle the 1-digit case too.
It's a more readable practice to use parseInt() for making a number from a digit, otherwise the implementation of the comment:
function isJumping(number) {
let str = number.toString();
for (let i = 1; i < str.length; i++) {
if (Math.abs(parseInt(str[i-1]) - parseInt(str[i])) !== 1){
return 'NOT JUMPING';
}
}
return 'JUMPING';
}
console.log(isJumping(345));
console.log(isJumping(3));
console.log(isJumping(79));

You just need to check your single digit case, and then see if all the digits vary by just 1
function isJumping(number) {
let str = number.toString();
if(str.length == 1)
return 'JUMPING'
const allByOne = str.substring(1).split('').every( (x,i) => {
var prev = str[i];
return Math.abs( +x - +prev) == 1
})
return allByOne ? 'JUMPING' : 'NOT JUMPING';
}
console.log(isJumping(9));
console.log(isJumping(79));
console.log(isJumping(23454));

A vaguely functional approach... The find gets position of the first character pair where the gap is more than one. The .filter deals with negatives (and other extraneous characters) by ignoring them.
// default b to a, so that last digit case, where b===undefined, gives true
const gapIsMoreThanOne = (a,b=a) => ( Math.abs(a-b)>1);
const isDigit = n => /[0-9]/.test(n);
const isJumping = n => n.toString()
.split("")
.filter(isDigit)
.find((x,i,arr)=>gapIsMoreThanOne(x,arr[i+1]))
=== undefined
? "JUMPING" : "NOT JUMPING"
;
console.log(isJumping(1)); // JUMPING
console.log(isJumping(12)); // JUMPING
console.log(isJumping(13)); // NOT JUMPING
console.log(isJumping(21)); // JUMPING
console.log(isJumping(21234565)); // JUPING
console.log(isJumping(-21234568)); // NOT JUMPING
console.log(isJumping("313ADVD")); // NOT JUMPING
PS: To me "JUMPING" implies that there is a gap greater than one, not that there isn't: but I've gone with how it is in the question.

Related

Recursive approach to Persistent bugger problem returns undefined

I've been trying to solve the following problem in codewars using recursion:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
For example (Input --> Output):
39 --> 3 (because 3*9 = 27, 2*7 = 14, 1*4 = 4 and 4 has only one digit)
999 --> 4 (because 9*9*9 = 729, 7*2*9 = 126, 1*2*6 = 12, and finally 1*2 = 2)
4 --> 0 (because 4 is already a one-digit number)
Here's what I've tried:
var numOfIterations = 0;
function persistence(num) {
//code me
var i;
var digits=[];
var result = 1;
if (num.toString().length==1) {
return numOfIterations;
} else {
numOfIterations++;
digits = Array.from(String(num), Number);
for (i=0;i<digits.size;i++) {
result=result*digits[i];
}
persistence(result);
}
}
But for some reason, instead of returning the number of iterations, it returns undefined. I've been told that I'm not using recursion correctly, but I just can't find the problem.
Other answers have explained what's wrong with your code. I just want to point out a simpler implementation:
const multiplyDigits = (n) =>
n < 10 ? n : (n % 10) * multiplyDigits (n / 10 | 0);
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (multiplyDigits (n));
[39, 999, 4] .forEach (t => console .log (`${t}:\t${persistence (t)}`));
multiplyDigits does just what it says, recursively multiplying the final digit by the number left when you remove that last digit (Think of | 0 as like Math .floor), and stopping when n is a single digit.
persistence checks to see if we're already a single digit, and if so, returns zero. If not, we add one to the value we get when we recur on the multiple of the digits.
I've been told that I'm not using recursion correctly
You're recursing, but you're not returning the result of that recursion. Imagine for a moment just this structure:
function someFunc() {
if (someCondition) {
return 1;
} else {
anotherFunc();
}
}
If someCondition is false, what does someFunc() return? Nothing. So it's result is undefined.
Regardless of any recursion, at its simplest if you want to return a result from a function then you need to return it:
function persistence(num) {
//...
if (num.toString().length==1) {
//...
} else {
//...
return persistence(result); // <--- here
}
}
As #David wrote in his answer, you were missing the return of the recursive call to itself.
Plus you were using digits.size instead of digits.length.
Anyway consider that one single digit being zero will collpse the game because that's enough to set the result to zero despite how many digits the number is made of.
To deal with the reset of numOfIterations, at first I tried using function.caller to discriminate between recursive call and direct call and set the variable accordingly. Since that method is deprecated as shown here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
I opted for the optional argument iteration that gets set to zero as default, to keep track of that value while it goes down the call stack. This solution still fulfills the fact that the caller doesn't need to know a new interface for the function to work.
//var numOfIterations = 0;
function persistence(num, iteration=0) {
/*
Commented strategy using the function.caller
working but deprecated so I can't recommend anymore
used optional argument iteration instead
//gets the name of the caller scope
let callerName = persistence.caller?.name;
//if it's different from the name of this function
if (callerName !== 'persistence')
//reset the numOfIterations
numOfIterations = 0;
*/
var digits=[];
if (num.toString().length==1){
return iteration;
} else {
var result = 1;
digits = Array.from(String(num), Number);
for (let i=0;i<digits.length;i++) {
result = result * digits[i];
}
return persistence(result, iteration+1);
}
}
console.log( persistence(39) ); //-> 3
console.log( persistence(999 ) ); //-> 4
console.log( persistence(4) ); //-> 0
You can do something like this
const persistenceTailRecursive = (num, iterations = 0) => {
const str = '' + num;
if(str.length === 1){
return iterations;
}
return persistenceTailRecursive(str.split('').reduce((res, a) => res * parseInt(a), 1), iterations + 1)
}
const persistence = (num) => {
const str = '' + num;
if(str.length === 1){
return 0;
}
return 1 + persistence(str.split('').reduce((res, a) => res * parseInt(a), 1))
}
console.log(persistenceTailRecursive(93))
console.log(persistenceTailRecursive(999))
console.log(persistence(93))
console.log(persistence(999))
There are 2 versions
1 tailRecursive call the same method with the exact signature (preventing stackoverflow in some languages like scala)
2 basic the result is calculated at the end

javascript external called function does not return value [duplicate]

Im solving a codewars problem and im pretty sure i've got it working:
function digital_root(n) {
// ...
n = n.toString();
if (n.length === 1) {
return parseInt(n);
} else {
let count = 0;
for (let i = 0; i < n.length; i++) {
//console.log(parseInt(n[i]))
count += parseInt(n[i]);
}
//console.log(count);
digital_root(count);
}
}
console.log(digital_root(942));
Essentially it's supposed to find a "digital root":
A digital root is the recursive sum of all the digits in a number.
Given n, take the sum of the digits of n. If that value has two
digits, continue reducing in this way until a single-digit number is
produced. This is only applicable to the natural numbers.
So im actually getting the correct answer at the end but for whatever reason on the if statement (which im watching the debugger run and it does enter that statement it will say the return value is the correct value.
But then it jumps out of the if statement and tries to return from the main digital_root function?
Why is this? shouldn't it break out of this when it hits the if statement? Im confused why it attempt to jump out of the if statement and then try to return nothing from digital_root so the return value ends up being undefined?
You're not returning anything inside else. It should be:
return digital_root(count);
^^^^^^^
Why?
digital_root is supposed to return something. If we call it with a one digit number, then the if section is executed, and since we return from that if, everything works fine. But if we provide a number composed of more than one digit then the else section get executed. Now, in the else section we calculate the digital_root of the count but we don't use that value (the value that should be returned). The line above could be split into two lines of code that makes it easy to understand:
var result = digital_root(count); // get the digital root of count (may or may not call digital_root while calculating it, it's not owr concern)
return result; // return the result of that so it can be used from the caller of digital_root
Code review
My remarks is code comments below
// javascript generally uses camelCase for function names
// so this should be digitalRoot, not digital_root
function digital_root(n) {
// variable reassignment is generally frowned upon
// it's somewhat silly to convert a number to a string if you're just going to parse it again
n = n.toString();
if (n.length === 1) {
// you should always specify a radix when using parseInt
return parseInt(n);
} else {
let count = 0;
for (let i = 0; i < n.length; i++) {
//console.log(parseInt(n[i]))
count += parseInt(n[i]);
}
// why are you looping above but then using recursion here?
// missing return keyword below
digital_root(count);
}
}
console.log(digital_root(942));
Simple recursive solution
With some of those things in mind, let's simplify our approach to digitalRoot...
const digitalRoot = n =>
n < 10 ? n : digitalRoot(n % 10 + digitalRoot((n - n % 10) / 10))
console.log(digitalRoot(123)) // => 6
console.log(digitalRoot(1234)) // 10 => 1
console.log(digitalRoot(12345)) // 15 => 6
console.log(digitalRoot(123456)) // 21 => 3
console.log(digitalRoot(99999999999)) // 99 => 18 => 9
Using reduce
A digital root is the recursive sum of all the digits in a number. Given n, take the sum of the digits of n. If that value has two digits, continue reducing in this way until a single-digit number is produced. This is only applicable to the natural numbers.
If you are meant to use an actual reducing function, I'll show you how to do that here. First, we'll make a toDigits function which takes an integer, and returns an Array of its digits. Then, we'll implement digitalRoot by reducing those those digits using an add reducer initialized with the empty sum, 0
// toDigits :: Int -> [Int]
const toDigits = n =>
n === 0 ? [] : [...toDigits((n - n % 10) / 10), n % 10]
// add :: (Number, Number) -> Number
const add = (x,y) => x + y
// digitalRoot :: Int -> Int
const digitalRoot = n =>
n < 10 ? n : digitalRoot(toDigits(n).reduce(add, 0))
console.log(digitalRoot(123)) // => 6
console.log(digitalRoot(1234)) // 10 => 1
console.log(digitalRoot(12345)) // 15 => 6
console.log(digitalRoot(123456)) // 21 => 3
console.log(digitalRoot(99999999999)) // 99 => 18 => 9
its a recursive function the code should be somewhat like this
function digital_root(n) {
// ...
n=n.toString();
if(n.length === 1){
return parseInt(n);
}
else
{
let count = 0;
for(let i = 0; i<n.length;i++)
{
//console.log(parseInt(n[i]))
count+=parseInt(n[i]);
}
//console.log(count);
return digital_root(count);
}
}
you should return the same function instead of just calling it to get the correct call stack

Is there a way to avoid number to string conversion & nested loops for performance?

I just took a coding test online and this one question really bothered me. My solution was correct but was rejected for being unoptimized. The question is as following:
Write a function combineTheGivenNumber taking two arguments:
numArray: number[]
num: a number
The function should check all the concatenation pairs that can result in making a number equal to num and return their count.
E.g. if numArray = [1, 212, 12, 12] & num = 1212 then we will have return value of 3 from combineTheGivenNumber
The pairs are as following:
numArray[0]+numArray[1]
numArray[2]+numArray[3]
numArray[3]+numArray[2]
The function I wrote for this purpose is as following:
function combineTheGivenNumber(numArray, num) {
//convert all numbers to strings for easy concatenation
numArray = numArray.map(e => e+'');
//also convert the `hay` to string for easy comparison
num = num+'';
let pairCounts = 0;
// itereate over the array to get pairs
numArray.forEach((e,i) => {
numArray.forEach((f,j) => {
if(i!==j && num === (e+f)) {
pairCounts++;
}
});
});
return pairCounts;
}
console.log('Test 1: ', combineTheGivenNumber([1,212,12,12],1212));
console.log('Test 2: ', combineTheGivenNumber([4,21,42,1],421));
From my experience, I know conversion of number to string is slow in JS, but I am not sure whether my approach is wrong/lack of knowledge or does the tester is ignorant of this fact. Can anyone suggest further optimization of the code snipped?
Elimination of string to number to string will be a significant speed boost but I am not sure how to check for concatenated numbers otherwise.
Elimination of string to number to string will be a significant speed boost
No, it won't.
Firstly, you're not converting strings to numbers anywhere, but more importantly the exercise asks for concatenation so working with strings is exactly what you should do. No idea why they're even passing numbers. You're doing fine already by doing the conversion only once for each number input, not every time your form a pair. And last but not least, avoiding the conversion will not be a significant improvement.
To get a significant improvement, you should use a better algorithm. #derpirscher is correct in his comment: "[It's] the nested loop checking every possible combination which hits the time limit. For instance for your example, when the outer loop points at 212 you don't need to do any checks, because regardless, whatever you concatenate to 212, it can never result in 1212".
So use
let pairCounts = 0;
numArray.forEach((e,i) => {
if (num.startsWith(e)) {
//^^^^^^^^^^^^^^^^^^^^^^
numArray.forEach((f,j) => {
if (i !== j && num === e+f) {
pairCounts++;
}
});
}
});
You might do the same with suffixes, but it becomes more complicated to rule out concatenation to oneself there.
Optimising further, you can even achieve a linear complexity solution by putting the strings in a lookup structure, then when finding a viable prefix just checking whether the missing part is an available suffix:
function combineTheGivenNumber(numArray, num) {
const strings = new Map();
for (const num of numArray) {
const str = String(num);
strings.set(str, 1 + (strings.get(str) ?? 0));
}
const whole = String(num);
let pairCounts = 0;
for (const [prefix, pCount] of strings) {
if (!whole.startsWith(prefix))
continue;
const suffix = whole.slice(prefix.length);
if (strings.has(suffix)) {
let sCount = strings.get(suffix);
if (suffix == prefix) sCount--; // no self-concatenation
pairCounts += pCount*sCount;
}
}
return pairCounts;
}
(the proper handling of duplicates is a bit difficile)
I like your approach of going to strings early. I can suggest a couple of simple optimizations.
You only need the numbers that are valid "first parts" and those that are valid "second parts"
You can use the javascript .startsWith and .endsWith to test for those conditions. All other strings can be thrown away.
The lengths of the strings must add up to the length of the desired answer
Suppose your target string is 8 digits long. If you have 2 valid 3-digit "first parts", then you only need to know how many valid 5-digit "second parts" you have. Suppose you have 9 of them. Those first parts can only combine with those second parts, and give you 2 * 9 = 18 valid pairs.
You don't actually need to keep the strings!
It struck me that if you know you have 2 valid 3-digit "first parts", you don't need to keep those actual strings. Knowing that they are valid 2-digit first parts is all you need to know.
So let's build an array containing:
How many valid 1-digit first parts do we have?,
How many valid 2-digit first parts do we have?,
How many valid 3-digit first parts do we have?,
etc.
And similarly an array containing the number of valid 1-digit second parts, etc.
X first parts and Y second parts can be combined in X * Y ways
Except if the parts are the same length, in which case we are reusing the same list, and so it is just X * (Y-1).
So not only do we not need to keep the strings, but we only need to do the multiplication of the appropriate elements of the arrays.
5 1-char first parts & 7 3-char second parts = 5 * 7 = 35 pairs
6 2-char first part & 4 2-char second parts = 6 * (4-1) = 18 pairs
etc
So this becomes extremely easy. One pass over the strings, tallying the "first part" and "second part" matches of each length. This can be done with an if and a ++ of the relevant array element.
Then one pass over the lengths, which will be very quick as the array of lengths will be very much shorter than the array of actual strings.
function combineTheGivenNumber(numArray, num) {
const sElements = numArray.map(e => "" + e);
const sTarget = "" + num;
const targetLength = sTarget.length
const startsByLen = (new Array(targetLength)).fill(0);
const endsByLen = (new Array(targetLength)).fill(0);
sElements.forEach(sElement => {
if (sTarget.startsWith(sElement)) {
startsByLen[sElement.length]++
}
if (sTarget.endsWith(sElement)) {
endsByLen[sElement.length]++
}
})
// We can now throw away the strings. We have two separate arrays:
// startsByLen[1] is the count of strings (without attempting to remove duplicates) which are the first character of the required answer
// startsByLen[2] similarly the count of strings which are the first 2 characters of the required answer
// etc.
// and endsByLen[1] is the count of strings which are the last character ...
// and endsByLen[2] is the count of strings which are the last 2 characters, etc.
let pairCounts = 0;
for (let firstElementLength = 1; firstElementLength < targetLength; firstElementLength++) {
const secondElementLength = targetLength - firstElementLength;
if (firstElementLength === secondElementLength) {
pairCounts += startsByLen[firstElementLength] * (endsByLen[secondElementLength] - 1)
} else {
pairCounts += startsByLen[firstElementLength] * endsByLen[secondElementLength]
}
}
return pairCounts;
}
console.log('Test 1: ', combineTheGivenNumber([1, 212, 12, 12], 1212));
console.log('Test 2: ', combineTheGivenNumber([4, 21, 42, 1], 421));
Depending on a setup, the integer slicing can be marginally faster
Although in the end it falls short
Also, when tested on higher N values, the previous answer exploded in jsfiddle. Possibly a memory error.
As far as I have tested with both random and hand-crafted values, my solution holds. It is based on an observation, that if X, Y concantenated == Z, then following must be true:
Z - Y == X * 10^(floor(log10(Y)) + 1)
an example of this:
1212 - 12 = 1200
12 * 10^(floor((log10(12)) + 1) = 12 * 10^(1+1) = 12 * 100 = 1200
Now in theory, this should be faster then manipulating strings. And in many other languages it most likely would be. However in Javascript as I just learned, the situation is a bit more complicated. Javascript does some weird things with casting that I haven't figured out yet. In short - when I tried storing the numbers(and their counts) in a map, the code got significantly slower making any possible gains from this logarithm shenanigans evaporate. Furthermore, storing them in a custom-crafted data structure isn't guaranteed to be faster since you have to build it etc. Also it would be quite a lot of work.
As it stands this log comparison is ~ 8 times faster in a case without(or with just a few) matches since the quadratic factor is yet to kick in. As long as the possible postfix count isn't too high, it will outperform the linear solution. Unfortunately it is still quadratic in nature with the breaking point depending on a total number of strings as well as their length.
So if you are searching for a needle in a haystack - for example you are looking for a few pairs in a huge heap of numbers, this can help. In the other case of searching for many matches, this won't help. Similarly, if the input array was sorted, you could use binary search to push the breaking point further up.
In the end, unless you manage to figure out how to store ints in a map(or some custom implementation of it) in a way that doesn't completely kill the performance, the linear solution of the previous answer will be faster. It can still be useful even with the performance hit if your computation is going to be memory heavy. Storing numbers takes less space then storing strings.
var log10 = Math.log(10)
function log10floored(num) {
return Math.floor(Math.log(num) / log10)
}
function combineTheGivenNumber(numArray, num) {
count = 0
for (var i=0; i!=numArray.length; i++) {
let portion = num - numArray[i]
let removedPart = Math.pow(10, log10floored(numArray[i]))
if (portion % (removedPart * 10) == 0) {
for (var j=0; j!=numArray.length; j++) {
if (j != i && portion / (removedPart * 10) == numArray[j] ) {
count += 1
}
}
}
}
return count
}
//The previous solution, that I used for timing, comparison and check purposes
function combineTheGivenNumber2(numArray, num) {
const strings = new Map();
for (const num of numArray) {
const str = String(num);
strings.set(str, 1 + (strings.get(str) ?? 0));
}
const whole = String(num);
let pairCounts = 0;
for (const [prefix, pCount] of strings) {
if (!whole.startsWith(prefix))
continue;
const suffix = whole.slice(prefix.length);
if (strings.has(suffix)) {
let sCount = strings.get(suffix);
if (suffix == prefix) sCount--; // no self-concatenation
pairCounts += pCount*sCount;
}
}
return pairCounts;
}
var myArray = []
for (let i =0; i!= 10000000; i++) {
myArray.push(Math.floor(Math.random() * 1000000))
}
var a = new Date()
t1 = a.getTime()
console.log('Test 1: ', combineTheGivenNumber(myArray,15285656));
var b = new Date()
t2 = b.getTime()
console.log('Test 2: ', combineTheGivenNumber2(myArray,15285656));
var c = new Date()
t3 = c.getTime()
console.log('Test1 time: ', t2 - t1)
console.log('test2 time: ', t3 - t2)
Small update
As long as you are willing to take a performance hit with the setup and settle for the ~2 times performance, using a simple "hashing" table can help.(Hashing tables are nice and tidy, this is a simple modulo lookup table. The principle is similar though.)
Technically this isn't linear, practicaly it is enough for the most cases - unless you are extremely unlucky and all your numbers fall in the same bucket.
function combineTheGivenNumber(numArray, num) {
count = 0
let size = 1000000
numTable = new Array(size)
for (var i=0; i!=numArray.length; i++) {
let idx = numArray[i] % size
if (numTable[idx] == undefined) {
numTable[idx] = [numArray[i]]
} else {
numTable[idx].push(numArray[i])
}
}
for (var i=0; i!=numArray.length; i++) {
let portion = num - numArray[i]
let removedPart = Math.pow(10, log10floored(numArray[i]))
if (portion % (removedPart * 10) == 0) {
if (numTable[portion / (removedPart * 10) % size] != undefined) {
let a = numTable[portion / (removedPart * 10) % size]
for (var j=0; j!=a.length; j++) {
if (j != i && portion / (removedPart * 10) == a[j] ) {
count += 1
}
}
}
}
}
return count
}
Here's a simplified, and partially optimised approach with 2 loops:
// let's optimise 'combineTheGivenNumber', where
// a=array of numbers AND n=number to match
const ctgn = (a, n) => {
// convert our given number to a string using `toString` for clarity
// this isn't entirely necessary but means we can use strict equality later
const ns = n.toString();
// reduce is an efficient mechanism to return a value based on an array, giving us
// _=[accumulator], na=[array number] and i=[index]
return a.reduce((_, na, i) => {
// convert our 'array number' to an 'array number string' for later concatenation
const nas = na.toString();
// iterate back over our array of numbers ... we're using an optimised/reverse loop
for (let ii = a.length - 1; ii >= 0; ii--) {
// skip the current array number
if (i === ii) continue;
// string + number === string, which lets us strictly compare our 'number to match'
// if there's a match we increment the accumulator
if (a[ii] + nas === ns) ++_;
}
// we're done
return _;
}, 0);
}

Find the parity outlier Javascript

My function fails (returns undefined) when the answer is a negative odd number only.
Otherwise it works.
Can anyone see why?
Instructions:
You are given an array (which will have a length of at least 3, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this "outlier" N.
My code:
function findOutlier(integers) {
let binary = integers.map((int, i) => int % 2);
let count = 0;
for (let i = 0; i < binary.length; i++) {
if (binary[i] == 0)
count++;
}
if (count > 1) {
return integers[binary.indexOf(1)]
} else {
return integers[binary.indexOf(0)]
}
}
The JavaScript % operator returns negative numbers in certain cases (when the left-hand side is negative and the right is positive). Thus your .indexOf(1) won't find the -1 in the array.
You could fix it in the .map() callback by using (i) => i & 1 to directly check the least-significant bit.
If it were me, I would interpret the warning in the assignment that the array could be "very large" as a warning that iteration should be minimized. Thus I'd be tempted to approach the problem differently. Once you've seen more than one even number or more than one odd number, you can assume that the first number that doesn't fit the pattern is the outlier. (Oh, and it occurs to me that the provision that the array always has at least 3 elements is another hint at the desired solution: you only need to check the first 3 elements to determine whether the input array is almost-all-even or almost-all-odd.)
So maybe something like:
function outlier(integers) {
function par(i) { return i & 1; }
let parity = par(integers[0]);
if (parity != par(integers[1])) {
if (parity == par(integers[2]))
// [0] and [2] are the true parity so [1] is the outlier
return integers[1];
// [1] and [2] are the true parity so [0] is the outlier
return integers[0];
}
return integers.find((i) => par(i) != parity);
}
Javascript % will return negative numbers when you use negative numbers on left hand side of %.
For example -3%2 = -1.
To handle such case you can change your code like:
function findOutlier(integers) {
let binary = integers.map((int, i) => Math.abs(int) % 2 );
let count = 0;
for (let i = 0; i < binary.length; i++) {
if (binary[i] == 0)
count++;
}
if (count > 1) {
return integers[binary.indexOf(1)]
} else {
return integers[binary.indexOf(0)]
}
}
This can work but there are better ways to solve the above problem with better time complexity. As mentioned in other answers(You only need 3 passes to decide outlier will be even or odd)
You could take an object for the storing of the first two indices of either odd (false) or even (true) values and exit early if at least two of one and one of the other type is found.
const isEven = value => value % 2 === 0;
function find(array) {
var indices = { true: [], false: [] },
i, key;
for (i = 0; i < array.length; i++) {
key = isEven(array[i]);
if (indices[key].length < 2) indices[key].push(i);
if (indices.true.length > 1 && indices.false.length === 1) return indices.false[0];
if (indices.false.length > 1 && indices.true.length === 1) return indices.true[0];
}
return indices;
}
console.log(find([1, 0, 0]));
console.log(find([0, 1, 0]));
console.log(find([0, 0, 1]));
console.log(find([0, 1, 1]));
console.log(find([1, 0, 1]));
console.log(find([1, 1, 0]));

Javascript recursive function not returning value?

Im solving a codewars problem and im pretty sure i've got it working:
function digital_root(n) {
// ...
n = n.toString();
if (n.length === 1) {
return parseInt(n);
} else {
let count = 0;
for (let i = 0; i < n.length; i++) {
//console.log(parseInt(n[i]))
count += parseInt(n[i]);
}
//console.log(count);
digital_root(count);
}
}
console.log(digital_root(942));
Essentially it's supposed to find a "digital root":
A digital root is the recursive sum of all the digits in a number.
Given n, take the sum of the digits of n. If that value has two
digits, continue reducing in this way until a single-digit number is
produced. This is only applicable to the natural numbers.
So im actually getting the correct answer at the end but for whatever reason on the if statement (which im watching the debugger run and it does enter that statement it will say the return value is the correct value.
But then it jumps out of the if statement and tries to return from the main digital_root function?
Why is this? shouldn't it break out of this when it hits the if statement? Im confused why it attempt to jump out of the if statement and then try to return nothing from digital_root so the return value ends up being undefined?
You're not returning anything inside else. It should be:
return digital_root(count);
^^^^^^^
Why?
digital_root is supposed to return something. If we call it with a one digit number, then the if section is executed, and since we return from that if, everything works fine. But if we provide a number composed of more than one digit then the else section get executed. Now, in the else section we calculate the digital_root of the count but we don't use that value (the value that should be returned). The line above could be split into two lines of code that makes it easy to understand:
var result = digital_root(count); // get the digital root of count (may or may not call digital_root while calculating it, it's not owr concern)
return result; // return the result of that so it can be used from the caller of digital_root
Code review
My remarks is code comments below
// javascript generally uses camelCase for function names
// so this should be digitalRoot, not digital_root
function digital_root(n) {
// variable reassignment is generally frowned upon
// it's somewhat silly to convert a number to a string if you're just going to parse it again
n = n.toString();
if (n.length === 1) {
// you should always specify a radix when using parseInt
return parseInt(n);
} else {
let count = 0;
for (let i = 0; i < n.length; i++) {
//console.log(parseInt(n[i]))
count += parseInt(n[i]);
}
// why are you looping above but then using recursion here?
// missing return keyword below
digital_root(count);
}
}
console.log(digital_root(942));
Simple recursive solution
With some of those things in mind, let's simplify our approach to digitalRoot...
const digitalRoot = n =>
n < 10 ? n : digitalRoot(n % 10 + digitalRoot((n - n % 10) / 10))
console.log(digitalRoot(123)) // => 6
console.log(digitalRoot(1234)) // 10 => 1
console.log(digitalRoot(12345)) // 15 => 6
console.log(digitalRoot(123456)) // 21 => 3
console.log(digitalRoot(99999999999)) // 99 => 18 => 9
Using reduce
A digital root is the recursive sum of all the digits in a number. Given n, take the sum of the digits of n. If that value has two digits, continue reducing in this way until a single-digit number is produced. This is only applicable to the natural numbers.
If you are meant to use an actual reducing function, I'll show you how to do that here. First, we'll make a toDigits function which takes an integer, and returns an Array of its digits. Then, we'll implement digitalRoot by reducing those those digits using an add reducer initialized with the empty sum, 0
// toDigits :: Int -> [Int]
const toDigits = n =>
n === 0 ? [] : [...toDigits((n - n % 10) / 10), n % 10]
// add :: (Number, Number) -> Number
const add = (x,y) => x + y
// digitalRoot :: Int -> Int
const digitalRoot = n =>
n < 10 ? n : digitalRoot(toDigits(n).reduce(add, 0))
console.log(digitalRoot(123)) // => 6
console.log(digitalRoot(1234)) // 10 => 1
console.log(digitalRoot(12345)) // 15 => 6
console.log(digitalRoot(123456)) // 21 => 3
console.log(digitalRoot(99999999999)) // 99 => 18 => 9
its a recursive function the code should be somewhat like this
function digital_root(n) {
// ...
n=n.toString();
if(n.length === 1){
return parseInt(n);
}
else
{
let count = 0;
for(let i = 0; i<n.length;i++)
{
//console.log(parseInt(n[i]))
count+=parseInt(n[i]);
}
//console.log(count);
return digital_root(count);
}
}
you should return the same function instead of just calling it to get the correct call stack

Categories

Resources