trying to figure out this coding problem:
Write an algorithm to determine if a number n is "happy".
A happy number is a number defined by the following process: Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1. Those numbers for which this process ends in 1 are happy numbers.
Return True if n is a happy number, and False if not.
I've done some work but I'm not sure what I'm doing incorrectly. Would appreciate some pointers. Thanks!
function isHappy(numba1){
let sum = 0;
numba = numba1.toString().split('')
let numbaArr = numba.map(y => parseInt(y))
for (var x = 0; x< numbaArr.length; x++){
sum += numbaArr[x] ** 2
}
if (sum > 1){
isHappy(sum)
}
else if (sum === 1){
return true
}
else if (sum <= 0){
return false
}
}
There are two problems I see with your answer, one small and one large.
Small: The value of the recursive call is not being returned. This:
if (sum > 1){
isHappy(sum)
}
should be
if (sum > 1){
return isHappy(sum)
}
Large: you are not doing the essential work of checking whether we're cycling over the same values. For instance in testing 15, we get these values
15, 26, 40, 16, 37, 58, 89, 145, 42, 20, 4, 16
^^ ^^
and we can quit because we've seen 16 twice. 15 is not happy. But for 44 we get
44, 32, 13, 10, 1
and we hit 1 without cycling, so 44 is a happy number.
Your code needs to keep track of the values it's seen so far.
Here's one recursive approach:
const digitsSquareSum = (n) =>
String (n) .split ('') .map (n => n * n) .reduce ((a, b) => a + b, 0)
const _isHappy = (n, seen) =>
n == 1
? true
: seen .has (n)
? false
: _isHappy (digitsSquareSum (n), seen .add (n))
const isHappy = (n) =>
_isHappy(n, new Set())
// display the happy numbers among the first 100 positive integers
console .log (Array .from ({length: 100}, (_, i) => i + 1) .filter (isHappy) .join(', '))
We use a helper function to calculate the sum of the squares of the digits. This simply makes the main function cleaner. The main function, _isHappy is an internal, private function, not to be exposed to the users. It is recursive and maintains a Set of the values we've already seen. If our number is 1, we return true. If our number is in the set of values we've already seen, we return false. Otherwise, we add it to the already seen set, calculate the next test case by calling our helper, and call _isHappy with those.
Our public function simply calls this main function, creating the initial empty Set of seen values, and passing that along with the number to test.
In our demo, we use Array .from ({length: 100}, (_, i) => i + 1), which is one of several compact ways of creating an array of integers from 1 to 100. In practice, I would abstract this into a range function that takes lo and hi values and creates an array of integers in between them, but that's outside the point of this answer.
We do not have to use this breakdown of an internal recursive function with two parameters and a public function with one. We could use a default parameter like this:
const isHappy = (n, seen = new Set()) =>
console .log({n, seen}) ||
n == 1
? true
: seen .has (n)
? false
: isHappy (digitsSquareSum (n), seen .add (n))
But there are some potential problems with this. For instance we could not call it like we did before:
range(1, 100) .filter (isHappy)
because filter supplies additional parameters to its callback. Not only does it supply the value but also the index and the whole array. However isHappy thinks the second parameter is the Set of seen values; when it gets passed the index, things will fail. We can do this instead:
range(1, 100) .filter ((n) => isHappy (n))
But we will always have to take such cautions when writing this way. I've gotten in the habit of doing this only for internal functions where I control how it's called. And still once in a while it bites me.
Keep a list of seen numbers and mod 10 to get the last digit then divide by 10 and floor to shift digits:
function sumSquares(num) {
let sum = 0;
while (num != 0) {
sum += (num % 10) ** 2;
num = Math.floor(num / 10);
}
return sum;
}
function isHappy(num) {
const seen = new Set();
while (num != 1 && !seen.has(num)) {
seen.add(num);
num = sumSquares(num);
}
return num == 1;
}
Related
I'm taking in an array of numbers [4, 6, 23, 10, 1, 3] I need to return true if any combinations of numbers in the array add up to the largest number in the array. So the example above should return true because 3 + 4 + 6 + 10 = 23
My thought behind this problem is I should first put the array in numerical order then make a new array with just the numbers I'm adding because I'm not adding the largest number. Then I need some method that says "If any combination of adding these numbers together equals the largest number in the original array return true". This is the code I've written so far, but I'm stuck on the for loop.. Any help would be very much appreciated!
function ArrayChallenge(arr){
let order = arr.sort(function(a, b){return a-b})
let addThese = order.slice(0,order.length-1)
for(i = 0; i < addThese.length-1; i++){
return true
}
}
console.log(ArrayChallenge([3,5,-1,8,12]))
It seems like a trick question. Here's the answer:
function ArrayChallenge(arr){
return true
}
Because the sum of just the largest number is always equal to the largest number.
If this solution is somehow not allowed, see subset sum problem:
there is a multiset S of integers and a target-sum T, and the question is to decide whether any subset of the integers sum to precisely T.
The most straightforward solution to that problem is with recursion; something like this:
function subsetSum(arr, sum) {
if (arr.length == 0) {
// No numbers left in the array. The only sum we can make is 0.
// If that is the sum we are seeking, great!
return sum == 0
} else {
// We will try both using and not using the first number in the array.
// The rest is passed to the recursive call.
const [current, ...remaining] = arr
return (
// Exclude current number, and recurse. We have to make the full
// sum with the remaining numbers.
subsetSum(remaining, sum) ||
// Include current number, meaning we have sum - current left
// to make with the remaining numbers.
subsetSum(remaining, sum - current)
)
}
}
I am trying to solve a problem on leetCode:
Given an unsorted integer array nums, return the smallest missing positive integer.
This is the code I came up with
var firstMissingPositive = function(nums) {
nums.sort();
let x = 1; //this is to compare the elements of nums
for (let num in nums) {
if (nums[num] <= 0) continue; //because anything less than 1 does not matter
else if (nums[num] != x) break; //if x is not present, x is the answer
else x++; // if x is present, x should increment and check for the next integer
}
return x;
};
This code works 106/173 testcases. It does not pass the following case, which looks very simple -
nums = [1,2,3,4,5,6,7,8,9,20];
The output I get is 3, whereas the expected output is 10.
I'm not looking for the right solution to the problem. I'm just curious why this seemingly simple test fails. I do not understand why my loop breaks at 3 when it passes 1 and 2. Please help!
Here's the root cause of your problem (mdn):
The sort() method sorts the elements of an array in place and returns
the sorted array. The default sort order is ascending, built upon
converting the elements into strings, then comparing their sequences
of UTF-16 code units values.
So what you get after sort is [1, 2, 20, 3, ...], as '20' string precedes '3' string. One possible way to fix this it to force sorting by numeric value:
nums.sort((a, b) => a - b);
It seems like Array.prototype.sort() is broken with BigInt
This works
const big = [1n, 2n, 3n, 4n];
big.sort();
console.log(big);
// expected output: Array [1n, 2n, 3n, 4n]
But this doesn't :(
const big = [1n, 2n, 3n, 4n];
big.sort((a,b)=>a-b);
console.log(big);
//Error: Cannot convert a BigInt value to a number
or am i doing something wrong?
JavaScript sort method requires a function as a parameter that can compare two elements of the array and return either a positive number, or a negative number or zero. Number is the keyword here.
BigInt operations like addition and subtraction returns BigInt type and not a Number type. And that's why the error you are getting.
So, Something like this should do the job
const big = [1n, 2n, 3n, 4n];
big.sort((a ,b) => {
if(a > b) {
return 1;
} else if (a < b){
return -1;
} else {
return 0;
}
});
console.log(big);
Interestingly, MDN document that I linked to previously, also suggests how to sort an array of BigInts, and it is concise:
Copying the whole section here for posterity:
const mixed = [4n, 6, -12n, 10, 4, 0, 0n]
// ↪ [4n, 6, -12n, 10, 4, 0, 0n]
mixed.sort() // default sorting behavior
// ↪ [ -12n, 0, 0n, 10, 4n, 4, 6 ]
mixed.sort((a, b) => a - b)
// won't work since subtraction will not work with mixed types
// TypeError: can't convert BigInt to number
// sort with an appropriate numeric comparator
mixed.sort((a, b) => (a < b) ? -1 : ((a > b) ? 1 : 0))
// ↪ [ -12n, 0, 0n, 4n, 4, 6, 10 ]
The reason is that a - b in the sort callback function will return a BigInt data type, while sort expects it to return something that is (or can coerce to) a Number data type.
So you can use a > b || -(a < b) as callback expression:
const big = [10n, 9n, 8n, 7n];
big.sort((a, b) => a > b || -(a < b));
console.log(big + ""); // 7,8,9,10
Note that the first version (without sort callback) does not work in general, because then sort will compare the elements as strings. It is clear that this can yield results that are not numerically sorted:
const big = [10n, 9n, 8n, 7n];
big.sort(); // string-based sort
console.log(big + ""); // 10,7,8,9 is wrong
This might appear to be broken at first but its not, the problem is definition of the compare function is always expecting a return value of -1, 0, 1 and it has no reason to expect bigint of -1 0 1 cause they all are in range of normal int..
so this should solve
big.sort((a,b)=>a<b?-1:(a>b?1:0));
The code below is modified version of a code taken from the book Professional JavaScript for Web Developers.
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
function numElements(typedArrayConstructor, ...typedArrays) {
// Count the total elements in all arrays
return typedArrays.reduce((x,y) => (x.length || x) + y.length);
}
console.log(numElements(Int32Array, Int8Array.of(1, 2, 3), Int16Array.of(4, 5, 6), Float32Array.of(7, 8, 9)));
My question is what does the (x.length || x) do? Why do we need to perform an or operation on x.length and x?
A little more explanation to go with Pointy's answer:
The || in JavaScript isn't just a logical OR operation, it deals with "truthy/falsey" values, not just booleans.
undefined is falsey. When the first operand of || is falsey, the second operand is evaluated, and becomes the result of the expression. Thus undefined || 0 equals 0.
In your sample code, this means when x is 0, you add 0, and get a proper numeric result. If you try to add to undefined to another number, all of your calculations turn into NaN after that.
When .reduce() is invoked with only one argument, the very first iteration uses element 0 as the first callback parameter and element 1 as the second.
In your case, that means that on the first iteration, x will be one of the arrays (note that y is always an array). Thus that little expression differentiates between when it's the first iteration and when it's a subsequent iteration by taking advantage of the fact that
someNumber.length
is always undefined. (As correctly noted in another answer, it's critical to recall that (undefined || x) will always be x, whatever its value may be.) On subsequent iterations therefore x is the running total of the array lengths.
The .reduce() could have been written as follows instead:
return typedArrays.reduce((x,y) => x + y.length, 0);
By passing the second argument (0) to .reduce(), the first callback invocation will be the same as the others, and x will always be a number.
If x has any elements and x exists then use the length in the sum. Otherwise if length is undefined then return the current element x
Example 1: - happens on first iteration of reduce loop
x is array [1, 2, 3]
x.length || x -> returns the length of array or current total
// summation of code would do the following
firsthArrayLength + secondArrayLength = newTotal
Example 2: - happens on rest of iterations of reduce loop
x is integer 5
x.length || x -> returns x since length of integer is undefined
// summation of code would do the following
currentTotal + curLength = newTotal
NOTE: Keep in mind that with this example if any of the arrays is null or undefined then it will throw since we cannot access property length of undefined or null
So sad that is taken from a book. Using reduce and || like that is reckless and unprofessional -
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
const numElements = (constructor, ...arrs) =>
new constructor(arrs.reduce((r, a) => r + a.length, 0))
const result =
numElements
( Int32Array
, Int8Array.of(1, 2, 3)
, Int16Array.of(4, 5, 6)
, Float32Array.of(7, 8, 9)
)
console.log(result.constructor)
console.log(result)
// Int32Array
// { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
The description of the function says it should concat the other arrays, not just initialise an empty typed array. Here's what that might look like -
// First argument is the type of array that should be returned
// Remaining arguments are all the typed arrays that should be concatenated
const concatMixed = (constructor, ...arrs) =>
{ const r = new constructor(arrs.reduce((r, a) => r + a.length, 0))
let i = 0
for (const a of arrs)
for (const val of a)
r[i++] = val
return r
}
const result =
concatMixed
( Int32Array
, Int8Array.of(1, 2, 3)
, Int16Array.of(4, 5, 6)
, Float32Array.of(7.1, 8.2, 9.3)
)
console.log(result.constructor)
console.log(result)
// Int32Array
// { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
I've found shuffling algorithm seems work fine.
const questions = [
{ name: "Ananda or Nalanda" },
{ name: "Sunny or Rainy" },
{ name: "Facebook or Instagram" },
{ name: "IOS or Android" },
{ name: "Mc or KFC" }
];
questions.sort(() => Math.random() - 0.5)
questions.forEach(e=>{
console.log(e.name)
})
But I couldn't think how this work in the syntax. I know that Math.random() will generate a number between 0 and 1. Also sort is standard function to sort. But how do these two functions shuffle my array? Why does it deduct 0.5 from Math.random()?
The return value from the .sort callback is expected to be a positive number, 0, or a negative number. So, subtracting 0.5 from a variable with a range of [0, 1) results in a range of [-0.5, 0.5) - an equal distribution of randomly sorting a before b, and of sorting b before a (where a and b are the elements being compared). This kind of randomly sorts the array, by randomly determining whether an a comes before or after a b.
If you didn't subtract 0.5, or subtracted something other than 0.5, the results would be significantly biased.
BUT, this is not a good way to randomly sort an array; the results here will be somewhat biased as well:
// an array of 'a' to 'f'
const questions = Array.from(
{ length: 6 },
(_, i) => String.fromCharCode(i + 97)
);
const positionFrequency = {};
for (let i = 0; i < 1e5; i++) {
const sorted = questions.slice().sort(() => Math.random() - 0.5);
sorted.forEach((char, i) => {
if (!positionFrequency[char]) {
positionFrequency[char] = {};
}
positionFrequency[char][i] = (positionFrequency[char][i] || 0) + 1;
});
}
console.log(positionFrequency);
Please run the snippet - it is very biased! In Chrome, a occurs in the first position some 28% of the time, despite the fact that it should occur there only 1/6th (16.667%) of the time. In Firefox 56, it's even more biased than that.
This is because the sorting algorithm is not stable - the results depend on which elements are compared against which other elements first (which is implementation-dependent). You can read more details about how exactly this kind of random-sorting is biased here:
http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html
Array.sort sorts an array given a compare function. The sort function compares all the items in the array using the compare function to determine which ones should go before other ones. The compare function must return a negative, zero, or positive value. It uses this value to determine which value should go first.
For example, when comparing values a and b, the sort function will call compare_function(a, b) and if it returns a negative value, sort will place a before b in the final sorted array. If the compare function returns a positive value, sort will place a after b.
So in your example, Math.random() - 0.5 is the compare function. Because Math.random() normally returns a value between 0 and 1, Math.random() - 0.5 will return a random number between -0.5 and 0.5. Therefore, the chance that the compare function (which is Math.random() - 0.5) will return a positive number is the same as the chance that the compare function will return a negative value.
In other words, a random number between -0.5 and +0.5 is used to determine whether an arbitrary item a in your array goes before or after an item b. And because the chances of a positive versus negative number being used are the same, the chances of a before b versus b before a, in the sorted array, are the same.
I hope this answer is helpful!