Codwars: Array to single value +1 - javascript

Given an array of integers of any length, return an array that has 1 added to the value represented by the array.
the array can't be empty
only non-negative, single digit integers are allowed
Return nil (or your language's equivalent) for invalid inputs.
Examples
For example the array [2, 3, 9] equals 239, adding one would return the array [2, 4, 0].
My code so far:
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return arr.some(e => typeof e !== 'number' || e < 0) ?
null : i
.toString()
.split('')
.map(e => parseInt(e));
};
It seems to pass most basic test however fails with larger inputs. Where have I gone wrong?

Just like you have converted the array into a number, you have to convert the number back into an array.
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return i.toString().split('').map(x => parseInt(x));
};
console.log(upArray([2,3,9]));

Your code won't work if the array length is greater than 100k...
Number type of javascript or any language is not enough big to handle it.
It's better if we calculate the last element with 1. If result is larger than nice ( < 10 ),
we continue to calculate next element with 1 and assign current value to 0. If result is smaller or equal 9, just assign the result to current and exit loop.
Then we print the final array as result:
pseudo code:
for i from: n-1:0
result = arr[i] + 1;
if(result < 10) :
arr[i] = result;
exit loop;// no need to continue calculate
else:
arr[i] = 0;
endif;
endfor;
You can join final array as string.

Here's probably the fastest solution (performance wise) - also there's no need to deal with BigInt, NaN, or Infinity:
function upArray(arr) {
if (!isInputIsNonEmptyArray(arr)) {
return null;
}
const isNumber = num => typeof num === 'number';
const isIntSingleDigit = num => Number.isInteger(num) && num >= 0 && num <10;
let resultArr = [];
let i = arr.length;
let num;
while (i-- > 0) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
if (num === 9) {
resultArr[i] = 0;
if (i === 0) { //means we're in the msb/left most digit, so we need to insert 1 to the left
resultArr.unshift(1);
break; //you can leave it out really, as the next check in the while will fail anyway
}
}
else {
resultArr[i] = num + 1; //No more + 1 should be made, just check for validity
//of the rest of the input and copy to the result arr
while (--i > -1) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
resultArr[i] = arr[i];
}
break;
}
}
return resultArr;
function isInputIsNonEmptyArray(arr) {
return Array.isArray(arr) && arr.length > 0;
}
}
If the input arg is not an array or an empty array, or if you encounter invalid element during the main while loop you return null.
In the main while loop you go from the right most element (lsd), and add 1 to it (or insert 0 if the number is 9) up the the left most digit.
If a number which is less than 9 is incremented, no need to increment any more (this is the while loop in the else clause).

Related

Time Complexity of my 2 pointer algorithm

For the given problem below, what would be the time complexity of my solution (below the problem). I think it is not O(n^2), but I am not sure if it is O(n).
Problem: You are given two strings s and t. You can select any substring of string s and rearrange the characters of the selected substring. Determine the minimum length of the substring of s such that string t is a substring of the selected substring.
Signature int minLengthSubstring(String s, String t)
Input s and t are non-empty strings that contain less than 1,000,000 characters each
Output Return the minimum length of the substring of s. If it is not possible, return -1
Example s = "dcbefebce" t = "fd" output = 5
Explanation: Substring "dcbef" can be rearranged to "cfdeb", "cefdb", and so on. String t is a substring of "cfdeb". Thus, the minimum length required is 5.
My code:
function minLengthSubstring(s, t) {
// Write your code here
if(t.length > s.length) return -1;
let letters = new Map();
for(const tc of t) {
letters.set(tc, (letters.get(t) || 0) +1);
}
let min = Infinity;
let start = 0;
let i = 0;
let count = 0;
let map = new Map(letters);
while(i < s.length) {
if(map.has(s[i])) {
if(start === 0 && count === 0) start = i;
count++;
if(map.get(s[i]) === 1) map.delete(s[i])
else map.set(s[i], map.get(s[i]) -1);
if(count === t.length) {
min = Math.min(min, i-start+1);
map = new Map(letters);
count = 0;
start++;
i = start;
}
} else {
i++;
}
}
return min === Infinity ? -1 : min;
}
Thank you.

Summing a number's digits

My code below. Can someone see the mistake I made?
Write a function named sumDigits which takes a number as input and returns the sum of the absolute value of each of the number's decimal digits. For example:
sumDigits(10); // Returns 1
sumDigits(99); // Returns 18
sumDigits(-32); // Returns 5
Let's assume that all numbers in the input will be integer values.
function sumDigits(number) {
let numberstring = number.toString()
let numberarr = numberstring.split("")
let counter = 0
for(var i=0; i<numberarr.length; i++) {
if(typeof numberarr[i] === "number") {
console.log(numberarr[i])
let numbervalue = Number(numberarr[i])
console.log(numbervalue)
counter += numbervalue
} else if (typeof numberarr[i] !== "number"){
counter += 0
}
}
return counter
}
console.log(Math.abs(sumDigits(10))); // Returns 1
console.log(Math.abs(sumDigits(99))); // Returns 1
console.log(Math.abs(sumDigits(-32))); // Returns 1 // Returns 5
//Let's assume that all numbers in the input will be integer values.
function sumDigits(number) {
var counter = 0;
var remainder;
number=Math.abs(number);
while(number>0){
counter=counter+number%10;
number=Math.floor(number/10);
}
return counter;
}
I think you are looking for some code like this. i don't get why you convert your number to string.
number=Math.abs(number); this line first convert any negative number to positive.
sumDigits(number) takes an argument as now loop through the number until number < 0 then add the remainder to counter variable then return counter as final sum
Try this solution:
console.log(sumDigits(-103)); // Returns 4
function sumDigits(number) {
var numberstring = number.toString();
var counter=0;
for(var i=0; i<numberstring.length; i++){
if(parseInt(numberstring.charAt(i))){
counter += parseInt(numberstring.charAt(i));
}
}
return counter;
}
This
if(typeof numberarr[i] === "number")
statement is always false, because before you convert the number into a string. I think that your function return always zero.
The answer by giovybus & Raman are correct, if you were wondering why your snippet didn't work - it's because the reference of the numberer[I] will always be a string. numberarr is a string array, not a number array, you needed to convert it to a number before using. Adjustments to your code above as below
sumDigits(10); // Returns 1
sumDigits(99); // Returns 18
sumDigits(-32);
function sumDigits(number) {
let numberstring = number.toString().replace("-", "");
let numberarr = numberstring.split("")
let counter = 0
for(var i=0; i<numberarr.length; i++) {
const numbervalue = Number(numberarr[i])
if(typeof numbervalue === "number") {
counter += numbervalue
} else if (typeof numbervalue !== "number"){
counter += 0
}
}
console.log(counter)
return counter
}
However this is not a good solution and you should consider using solution by giovybus, Raman or anyone else with better approach instead.
Using filter and reduce to calculate the total.
function sumDigits(number) {
return ( [...number.toString()]
.filter( (char) => !isNaN(parseInt( char ) ) )
.reduce( function(a,b) { return a + parseInt(b) }, 0) );
}
const sum1 = sumDigits(103);
console.log( sum1 );
const sum2 = sumDigits(-105);
console.log( sum2 );
Using latest ES6 syntax...
function sumDigits(num) {
return [...num.toString()].map(Number).reduce((acc, val) => acc + val, 0);
};
console.log(sumDigits(55)); // 10
Explanation of tricky parts:
This will split the string into an array of digits:
[..."1234"] // => [ "1", "2", "3", "4" ]
This will transform it into Numbers
[ "1", "2" ].map(Number) // => [ 1, 2 ]

count duplicate value in recursion

how to count the duplicate values in string/array by recursion, well i know how to find the duplicate and unique by the loop with the object, but in this case, i tried to recursion it to learn my logic,
here is what i tried
function duplicate(word) {
let [obj, arr, count ] = [{},[],0]
for (i of word) {
obj[i] = (obj[i] || 0) + 1
if (obj[i] == 2) {
arr.push(i);
}
}
// console.log(arr);
let words = [...word];
words = [...new Set(words)];
// // return word
if (words.length === 0) {
return count
} else if (arr.includes(words[0])) {
count++
}
return count + duplicate(words.slice(1))
}
console.log(duplicate('xmikemk')) // 2
i tried to create object fist and pust to newArr if the values found 2 times,
then i tried to SET the word in again below to just show the unique value, then i recursions it, when i console log it, the slice method on that below to recursion and is not working, that is why the ouput i got here is 0, what is wrong with my code ? am I wrong with that condition??
that variable word has = ('xmikemk') which has 2 duplicate words, m and k , so the output i want is2`
You could spread the given string and take only the first character for counting, by taking another parameter for handing over the last counted values.
If no more spreadable items, the count is made and returned. Otherwise go on with the recursive call.
function duplicate([w, ...rest], counter = {}) {
counter[w] = (counter[w] || 0) + 1;
if (!rest.length) {
return Object.values(counter).reduce((s, c) => s + (c > 1), 0);
}
return duplicate(rest, counter);
}
console.log(duplicate('xmikemk')) // 2
You are making this more complicated than it is. Just create an internal accumulator method to populate a map and increment the frequency.
function duplicate(word) {
return duplicateInternal(word, {});
}
function duplicateInternal(word, mapping) {
if (word == null || word.length === 0) {
return mapping;
}
var c = word.charAt(0);
mapping[c] = (mapping[c] || 0) + 1;
return duplicateInternal(word.substr(1), mapping);
}
var mapping = duplicate('xmikemk');
var frequency = Object.keys(mapping)
.map(key => [key, mapping[key]])
.sort((a, b) => {
let diff = -1 * (a[1] - b[1]);
return diff === 0 ? a[0].localeCompare(b[0]) : diff;
});
console.log(frequency.map(x => x.join(' x')).join('\n'))
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}

looping through split numbers javascript

I have this function, I want to loop through both of the strings and
receive a return value for compareNumber that's closest to it - that is (compareNumber +1) but only if none of it's digits are equal to any of searchNumber's digits. If any of compareNumber's digits are equal to any of searchNumber's digits, I need to fins the first value bigger than compareNumber that's not equal to any of searchNumber's digits.
function compareNumbers(searchNumber, compareNumber){
var isEqual = true;
var digitsCompare = compareNumber.toString().split('');
searchNumber.toString().split('').forEach(function(num,index) {
if(!(num===digitsCompare[index])){
isEqual = false;
}
});
return isEqual;
}
var b = compareNumbers(123,124);
console.log(b);
var d = compareNumbers(123,123);
console.log(d);
I think it would be best to consider this mathematically, as opposed to just adding 1 forever until you find a number that works.
The solution below iterates through each individual digit, increasing it by 1 until we find a digit we're allowed to use. Once we make that change, we know the rest of the digits will be replaced with the lowest number available to us.
Think of it like a combination lock, except as soon as you turn one dial, all the ones after it reset to the lowest number we're allowed to use.
function compareNumbers(n1, n2) {
var n1Array = ("" + n1).split("").map(Number);
var n2Array = ("" + n2).split("").map(Number);
var availableNumbers = [...Array(10).keys()].filter(n => !n1Array.includes(n));
//Loop through each digit in our compare string
n2Array.some((n, index) => {
let originalN = n;
//Increment it until we have a valid number
while (!availableNumbers.includes(n))
if (n < 9) n++
else {
//If we've passed 9, then we need to use the lowest number *twice*
//However, if we're changing the first number, we CAN'T replace it with a 0
n = Number((index === 0 ? availableNumbers[0] || availableNumbers[1] : availableNumbers[0]).toString() + availableNumbers[0]);
break;
}
if (originalN !== n) {
n2Array[index] = n;
var replacements = n2Array.splice(index + 1).map(n => availableNumbers[0]);
n2Array = [...n2Array, ...replacements];
return true; //Exit early
}
return false; //Keep iterating
});
//Turn [4,0,0] into 400
return Number(n2Array.join(""));
}
let result1 = compareNumbers(123,124);
console.log(result1);
let result2 = compareNumbers(123,423);
console.log(result2);
A lot of the scrappy/ugly stuff is to account for edge-cases.
The first edge-case being that if we increase a 9, then it shouldn't become a 10, but rather the lowest number available to us repeated twice.
However, there is an edge-case within that. If our lowest number available is 0, and 9 is our first number, we can't replace it with 00. Otherwise, you could end up with 915 becoming 0015.
The code is below:
function compareNumbers(searchNumber, compareNumber) {
//error check the args
var searchString = searchNumber + "";
var compareString = compareNumber + "";
if (compareString.length === 0) return "nope";
var compareInt = parseInt(compareString) + 1;
if (searchString.length === 0) {
return compareInt;
}
//don't crash the app
if (searchString.length >= 10
&& searchString.indexOf("0") >= 0 && searchString.indexOf("1") >= 0
&& searchString.indexOf("2") >= 0 && searchString.indexOf("3") >= 0
&& searchString.indexOf("4") >= 0 && searchString.indexOf("5") >= 0
&& searchString.indexOf("6") >= 0 && searchString.indexOf("7") >= 0
&& searchString.indexOf("8") >= 0 && searchString.indexOf("9") >= 0 ) {
return "nope";
}
while(containsDigits(searchString, compareInt)) {
compareInt++;
}
return compareInt;
}
function containsDigits(digits, intVal) {
var strVal = intVal + "";
var strDigits = digits + "";
for(var i = 0; i < strDigits.length; i++) {
if (strVal.indexOf(strDigits.charAt(i)) >= 0) {
return true;
}
}
return false;
}
// Examples
pairs = [[123, 124], [11, 13], [25, 35], [15, 21], [138, 546], [1, 2], [1, 1]];
pairs.forEach((pair) => console.log(`Compare [${pair[0]}, ${pair[1]}]: ${compareNumbers(pair[0], pair[1])}`));
You don't need to split a string to access its contents as an array.
var digitsCompare = compareNumber.toString();
console.log(digitsCompare.charAt(0)); <-- first char

To determine whether a number and any other two numbers in the array to form a continuous number of three

write a function like this:
function canFormContinuosNums(num, array);
//num is the target number,
//array is the source array which contain some int number:
for example:
num = 1,
array =[ 2,3,4,5,7,8,9,0];
the function must test weather the array contain two number such as 2,3 or 0,2, can form '012' or '123'.
The function must return false or true ;
if true return the two array ,like [2,3] or [0,2];
I tried a lot, but neither work perfect.Thanks a lot for your help.
How about this one?
function canFormContinuosNums(num, array) {
var tempArray = new Array();
var outputArray = new Array();
for (var i = 0; i < 3; i++) {
tempArray[i] = [num+i-2, num+i-1, num+i];
tempArray[i].splice(tempArray[i].indexOf(num), 1);
var check = 0;
for (var k = 0; k < tempArray[i].length; k++) {
if (array.includes(tempArray[i][k])) {
check += 1;
}
}
if (check == 2) {
outputArray.push(tempArray[i]);
}
}
console.log(outputArray);
};
num = 4,
array =[2,3,4,5,7,8,9,0];
canFormContinuosNums(num, array);
You can try something like this:
Logic:
Find the index where number is to be inserted.
Now loop over partial range:
Start from 0 or index-2 to capture previous elements.
Stop loop at index + 2. This may result in exceeding bounds.
Now validate, if value exists and the difference between next and current element is 1 increment count.
Now compare if count is greater than on equal to your required continuous length (2 in your case) and return accordingly.
function canMakeContinuous(arr, num) {
if(arr.indexOf(num) > -1) return false
var copy = arr.slice();
var index = 0;
arr.some(function(item, i) {
index = i;
if (item > num) {
return true;
}
});
copy.splice(index, 0, num)
var count = 0;
for (var i = Math.max(index - 2, 0); i < index + 3; i++) {
if (copy[i + 1] !== undefined && copy[i + 1] - copy[i] === 1) {
count++;
} else if (count < 2) {
count = 0;
}
}
return count > 1
}
var array = [2, 3, 4, 7, 8, 9, 0];
canMakeContinuous(array, 1)
canMakeContinuous(array, 6)
canMakeContinuous(array, 5)
canMakeContinuous(array, 9)
function canFormContinuousNums(num, array){
// array contains the 2 numbers before num
if(array.indexOf(num-2)!=-1 && array.indexOf(num-1)!=-1){
return [num-2,num-1];
}
// array contains the number before num AND the number after num
else if(array.indexOf(num-1)!=-1 && array.indexOf(num+1)!=-1){
return [num-1,num+1];
}
// array contains the 2 numbers after num
else if(array.indexOf(num+1)!=-1 && array.indexOf(num+2)!=-1){
return [num+1,num+2];
}
return false;
}
This should give you wether the array contains two numbers such as you can form a continuous number of three, and if you can, will return either the array containing the 2 previous numbers, or the one containing the one before and the one after, or the one containing the two next numbers.
You could also have a code for which combination is doable as :
0 -> no combination
1 -> the two previous numbers
2 -> the one before & the one after
4 -> the two next numbers
And then :
3 will give you both 1 & 2
5 will give you both 1 & 4 // note that 1 & 4 is impossible without 2
6 will give you both 2 & 4
7 will give you all three combinations.
As following :
function canFormContinuousNums(num, array){
var result = 0;
// array contains the 2 numbers before num
if(array.indexOf(num-2)!=-1 && array.indexOf(num-1)!=-1){
result += 1;
}
// array contains the number before num AND the number after num
if(array.indexOf(num-1)!=-1 && array.indexOf(num+1)!=-1){
result += 2;
}
// array contains the 2 numbers after num
if(array.indexOf(num+1)!=-1 && array.indexOf(num+2)!=-1){
result += 4;
}
return result;
}
var array = [0,1,4,6,8,9,11,14,15,17,18];
console.log(canFormContinuousNums(20, array)); // no combination
console.log(canFormContinuousNums(2, array)); // [0,1]
console.log(canFormContinuousNums(5, array)); // [4,6]
console.log(canFormContinuousNums(10, array)); // [8,9] & [9,11]
console.log(canFormContinuousNums(13, array)); // [14,15]
console.log(canFormContinuousNums(7, array)); // [6,8] & [8,9]
console.log(canFormContinuousNums(16, array)); // [14,15] & [15,17] & [17,18]
// test function to display all the combinations switch the number and the result given by canFormContinuousNums
function displayResult(number,result){
if(result & 1){
console.log("["+(number-2)+","+(number-1)+"]");
}
if(result & 2){
console.log("["+(number-1)+","+(number+1)+"]");
}
if(result & 4){
console.log("["+(number+1)+","+(number+2)+"]");
}
}
console.log("Test displayResult(16,7)");
displayResult(16,canFormContinuousNums(16,array));
Edit :
As you wish to get all the combinations, the following should work :
function canFormContinuousNums(num, array){
var flag = false;
var result = [[0,0],[0,0],[0,0]];
// array contains the 2 numbers before num
if(array.indexOf(num-2)!=-1 && array.indexOf(num-1)!=-1){
flag = true;
result[0][0] = num-2;
result[0][1] = num-1;
}
else{
result[0] = false;
}
// array contains the number before num AND the number after num
if(array.indexOf(num-1)!=-1 && array.indexOf(num+1)!=-1){
flag = true;
result[1][0] = num-1;
result[1][1] = num+1;
}
else{
result[1] = false;
}
// array contains the 2 numbers after num
if(array.indexOf(num+1)!=-1 && array.indexOf(num+2)!=-1){
flag = true;
result[2][0] = num+1;
result[2][1] = num+2;
}
else{
result[2] = false;
}
if(flag == true){
return result;
}
else{
return false;
}
}
var array2 = [0,1,2];
console.log(canFormContinuousNums(1,array2));
console.log(canFormContinuousNums(4,array2));

Categories

Resources