Why does my outer variable not change when using a while loop? - javascript

function ranNum(value) {
return Math.ceil(Math.random() * value)
}
function createRanId(value) {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('')
const numbers = '0123456789'.split('')
const idLength = value || 6
let id = ''
for(let i = 0; i < idLength; i++) {
const numOrAlpha = ranNum(2)
numOrAlpha === 1 ? id += alphabet[ranNum(alphabet.length - 1)] : id += numbers[ranNum(numbers.length - 1)]
}
return id
}
function isAllNumbers(arr) {
return arr.every(value => Number.isInteger(value))
}
function allNumberId() {
let count = 0
let ranNum = createRanId(2).split("");
while(!isAllNumbers(ranNum)) {
ranNum = createRanId(2).split("")
count++
}
return [count, ranNum]
}
console.log(allNumberId())
So what im doing is generating a random string that consists of numbers and letters (for example: 3e3jjf). What i'm trying to achieve is to find a generated combination that only consists of numbers (for example: 235033). However, my code doesnt seem to work and ends up in an infinite loop. I'm making a thinking error somewhere in the function allNumberId
edit: this is obviously not production code or anything. I'm just practicing some javascript. It bugs me that I cant find what I do wrong here.

In your code you are checking for a number
Number.isInteger("6")
When it is a string it is false. So you need to alter your code to try to make it into a number or other option is isNaN()
function ranNum(value) {
return Math.ceil(Math.random() * value)
}
function createRanId(value) {
const alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('')
const numbers = '0123456789'.split('')
const idLength = value || 6
let id = ''
for(let i = 0; i < idLength; i++) {
const numOrAlpha = ranNum(2)
numOrAlpha === 1 ? id += alphabet[ranNum(alphabet.length - 1)] : id += numbers[ranNum(numbers.length - 1)]
}
return id
}
function isAllNumbers(arr) {
return arr.every(value => Number.isInteger(+value))
}
function allNumberId() {
let count = 0
let ranNum = createRanId(2).split("");
while(!isAllNumbers(ranNum)) {
ranNum = createRanId(2).split("")
count++
}
return [count, ranNum]
}
console.log(allNumberId())
Your check could also be done as
const isInvalid = yourString.split("").map(Number).some(isNaN)

The problem is the usage of Number.isInteger. You're actually passing strings there (single-character strings consisting of a digit or a alphabet char), which is never a number (integer or not) so Number.isInteger always returns false and your isAllNumbers function doesn't recognice what it should.

Related

Why does this function not work within the eventListener?

I am just starting with JavaScript and for my first project I am trying to display a working calculator using JS, HTML and CSS. I am just one step away from it finally handling easy operations but for some reason my "solver"-function does not work when hitting the Enter-button.
I already tried out all the functions in a "test.js" console.logging the results and everything worked perfectly. But for some reason it won't work when combining it with the eventListener and trying to display it within the textfield. I also tried simpler functions and displaying only variables in the textfield which works. It is just the solver-function that won't.
I will attach my code - most of you would definitely code a calculator much different and especially much shorter than the way I did it but I am proud I got this far, so please don't judge me too harshly! :D
First the declaration of functions which worked perfectly in the test.js. The solver was originally designed to return the previous 4 functions in a nested way but I thought this might be the problem, so I changed it.
function adds(num1, num2) {
return num1+num2;
};
function subs(num1, num2) {
return num1-num2;
};
function muls(num1, num2) {
return num1*num2;
};
function divis(num1, num2) {
return num1/num2;
};
//Creates an array with string elements.
function createArray(string) {
let array = [];
for(let element of string) {
array.push((element));
}
return array;
};
//Returns an array where numbers are joint within one element (e.g. '1','2' becomes '12').
function joinNums(array) {
let numArray = [''];
let index = 0;
for (i=0; i < array.length; i++) {
if (isNaN(parseInt(array[i]))) {
index ++;
numArray.push(array[i]);
index++;
numArray[index]=[''];
continue;
}
numArray[index] =numArray[index] + array[i];
}
return numArray;
};
//Returns an array where all elements with numbers in them are changed to proper numbers instead of strings.
function makeNums(array) {
let numArray = [''];
let index = 0;
for (i=0; i < array.length; i++) {
if (isNaN(parseInt(array[i]))) {
index ++;
numArray.push(array[i]);
index++;
numArray[index]=[''];
continue;
}
numArray[index] = parseInt(array[i]);
}
return numArray;
};
//Calculates the array that is provided and returns a single number as solution.
function operate(array) {
let solution = array[0];
for(let iOp = 1; array.length >= iOp; iOp=iOp+2) {
if(array[iOp] === '+') {
solution = adds(solution, array[iOp+1]);
}
if(array[iOp] === '-') {
solution = subs(solution, array[iOp+1]);
}
if(array[iOp] === '*') {
solution = muls(solution, array[iOp+1]);
}
if(array[iOp] === '/') {
solution = divis(solution, array[iOp+1]);
}
}
return solution;
};
//Takes a string (meant to be the value of a textfield) and returns the solution by calling all previously declared helper functions.
function solver(string) {
let cr = createArray(string);
let jo = joinNums(cr);
let ma = makeNums(jo);
let op = operate(ma);
return op;
};
Now on to the input field and hitting the enter-button:
//This is the enter-button
let enter = document.getElementById("enter");
//This is the textfield where calculations are entered. The textfield is then meant to be changed to display the operations result.
let textfield = document.getElementById("resultLn");
//The eventlistener.
enter.addEventListener("click", () => {
textfield.value = solver(textfield.value);
});
You do not use let statement at the for cycles i operand (at function joinNums, makeNums).
In addition at function operate, you can use switch.
Hi,
Your calculator code works well, If there was a problem, checkout your html.
I test with some operations and it works well. If there any operation doesn't work, please type this operation to help you
//This is the enter-button
let enter = document.getElementById("enter");
//This is the textfield where calculations are entered. The textfield is then meant to be changed to display the operations result.
let textfield = document.getElementById("resultLn");
//The eventlistener.
enter.addEventListener("click", () => {
textfield.value = solver(textfield.value);
});
function adds(num1, num2) {
return num1+num2;
};
function subs(num1, num2) {
return num1-num2;
};
function muls(num1, num2) {
return num1*num2;
};
function divis(num1, num2) {
return num1/num2;
};
//Creates an array with string elements.
function createArray(string) {
let array = [];
for(let element of string) {
array.push((element));
}
return array;
};
//Returns an array where numbers are joint within one element (e.g. '1','2' becomes '12').
function joinNums(array) {
let numArray = [''];
let index = 0;
for (i=0; i < array.length; i++) {
if (isNaN(parseInt(array[i]))) {
index ++;
numArray.push(array[i]);
index++;
numArray[index]=[''];
continue;
}
numArray[index] =numArray[index] + array[i];
}
return numArray;
};
//Returns an array where all elements with numbers in them are changed to proper numbers instead of strings.
function makeNums(array) {
let numArray = [''];
let index = 0;
for (i=0; i < array.length; i++) {
if (isNaN(parseInt(array[i]))) {
index ++;
numArray.push(array[i]);
index++;
numArray[index]=[''];
continue;
}
numArray[index] = parseInt(array[i]);
}
return numArray;
};
//Calculates the array that is provided and returns a single number as solution.
function operate(array) {
let solution = array[0];
for(let iOp = 1; array.length >= iOp; iOp=iOp+2) {
if(array[iOp] === '+') {
solution = adds(solution, array[iOp+1]);
}
if(array[iOp] === '-') {
solution = subs(solution, array[iOp+1]);
}
if(array[iOp] === '*') {
solution = muls(solution, array[iOp+1]);
}
if(array[iOp] === '/') {
solution = divis(solution, array[iOp+1]);
}
}
return solution;
};
//Takes a string (meant to be the value of a textfield) and returns the solution by calling all previously declared helper functions.
function solver(string) {
let cr = createArray(string);
let jo = joinNums(cr);
let ma = makeNums(jo);
let op = operate(ma);
return op;
};
<input type="text" id="resultLn">
<button id="enter">Enter</button>

Check if String has sequential or repeated characters in javascript (underscore)

I have code that I am trying to refactor. Im new to javascript so Im tring to make more readable code using functions in libraries like underscore.
The function below can detect when string
contains 3 or more ordered characters such as (234, efg, LmN)
and
when string contains 3 or more repeated (lll, 444, MMm, ###)
const input = "Dfdf123125";
const myStr = input.toLowerCase();
const n = 3;
let isRepeating = false;
let isSequential = false;
for (let i = 0; i < myStr.length; i++) {
if (i + (n - 1) <= myStr.length) {
let isRepeatingTemp = false;
let isSequentialTemp = false;
for (let j = i; j < i + n; j++) {
(myStr.charCodeAt(i) === myStr.charCodeAt(j)) ? isRepeatingTemp = true: isRepeatingTemp = false;
(myStr.charCodeAt(i) === myStr.charCodeAt(j) - (n - 1)) ? isSequentialTemp = true : isSequentialTemp = false;
}
if (isRepeatingTemp) isRepeating = true;
if (isSequentialTemp) isSequential = true;
}
}
Im trying to to see if I can optimize this and make it more readable with underscore and/or even make time/space complexity better. I know this can also be done with regx but im trying to get it done without it.
Instead of the inner for loop, I chunked the string to n using Array.prototype.slice() to see ahead n characters. I used Array.prototype.indexOf() to find if it's sequential based off the abc and num constants(ref). To see if it's repeating, I used Array.prototype.every() that loops through the chunk and check if they're similar and return a boolean based on the expression.
The result gives the output of each instance found, and if it was sequential or repeating.
const input = "Dfdf123125";
function RepSeq(str, n) {
var rep = false;
var seq = false;
var result = [];
const num = '0123456789';
const abc = 'abcdefghijklmnopqrstuvqxyz';
if (str.length < n) return false;
for (var i = 0; i < str.length; i++) {
if (i + n > str.length) break;
var chunk = str.slice(i, i + n);
var seqABC = abc.indexOf(chunk) > -1;
var seq123 = num.indexOf(chunk) > -1;
if (seq123 || seqABC) {
seq = true;
result.push(chunk);
}
if ([...chunk].every(v => v.toLowerCase() === chunk[0].toLowerCase())) {
rep = true;
result.push(chunk);
}
}
return {
repetition: rep,
sequential: seq,
out: result
};
}
console.log(RepSeq(input, 3));
// Output:
// {
// out: ["123"],
// repetition: false,
// sequential: true
// }
With this method, we're peeking at the string one block(i+n) at a time. Ex(n=3):
1. [Dfd]f123125
2. D[fdf]123125
3. Df[df1]23125
4. Dfd[f12]3125
5. Dfdf[123]125 - Sequential!
6. Dfdf1[231]25
7. Dfdf12[312]5
8. Dfdf123[125]

Find how many repetetive letters there are in the text

My goal is to get the result of 3 since in "aaBbCChr" letters a,b and c are repeating themselves. I've made this function, everything looks right, but it just doesn't work properly.
function duplicateCount(text) {
let lettersArray = text.split("");
let duplicateResult = 0;
lettersArray.map(function(letter) {
let regexLetter = new RegExp(letter, "gi");
let matchesCount = text.match(regexLetter).length;
if (matchesCount > 1) {
duplicateResult + 1;
} else {};
});
return duplicateResult;
};
alert(duplicateCount("aaBbCChr"));
Issues with your code:
duplicateResult + 1; This doesn't change the value of duplicateResult. You need to use a statement that actually increases its value like duplicateResult += 1;
lettersArray also contains the letters multiple times. First remove the duplicates there before looking whether they appear multiple times in the string or you will count them multiple times. See Get all unique values in a JavaScript array (remove duplicates)
Make all lowercase letters in lettersArray so that you don't count the same letter twice when it appears in lowercase and uppercase.
This code works
function duplicateCount(text) {
let lettersArray = text.toLowerCase().split("");
let duplicateResult = 0;
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
lettersArray = lettersArray.filter( onlyUnique );
lettersArray.map(function(letter) {
let regexLetter = new RegExp(letter, "gi");
let matchesCount = text.match(regexLetter).length;
if (matchesCount > 1) {
duplicateResult += 1;
} else {};
});
return duplicateResult;
};
alert(duplicateCount("aaBbCChr"));
Another approach is to invert the logic used in Get all unique values in a JavaScript array (remove duplicates) to use filter to filter out all those characters that appear multiple times and then count those.
function duplicateCount2(text) {
let lettersArray = text.toLowerCase().split("");
let duplicateResult = 0;
function onlyNonUnique(value, index, self) {
return self.indexOf(value) !== index;
}
lettersArray = lettersArray.filter(onlyNonUnique);
duplicateResult = lettersArray.length;
return duplicateResult;
};
alert(duplicateCount2("aaBbCChr"));
It is far easier to do it with changing the structure to an object
const reduced = [...myChars].reduce((map, charCaseSensitive) => {
const char = charCaseSensitive.toLowerCase();
if(map[char]) map[char]++;
else map[char] = 1;
return map;
}, {};
const numberOfRepeatedChars = Object.values(reduced).filter(v => v > 1).length;
Here's the version making minimal changes to your code. You need to lowercase the string here so that the same letter in caps is not counted again.
function duplicateCount(text) {
let lettersArray = text.toLowerCase().split("");
let duplicateResult = 0;
let set = new Set();
lettersArray.map(function(letter) {
let regexLetter = new RegExp(letter, "gi");
let matchesCount = text.match(regexLetter).length;
if (!set.has(letter) && matchesCount > 1) {
set.add(letter);
duplicateResult += 1;
} else {};
});
return duplicateResult;
};
alert(duplicateCount("aaBbCChr"));
As mentioned in other comments, your problem is that you're resetting duplicateResult = 1 to a duplicate result. You probably meant to do duplicateResult += 1, but even that is incorrect because you said that you only want to get repeated characters, so no matter how many times a letter repeats, if it repeats at all, you only count it once.
My approach would be to count the characters in the string in an object (key is the letter, value is the occurrence count) and afterwards count the number of keys that have an occurrence count of more than 1.
function duplicateCount(text) {
const isValidLetter = /[a-z]/i; // a through z, case insensitive
const charCounts = text.split('').reduce(function(acc, char) {
if(isValidLetter.test(char)) {
char = char.toLowerCase();
if(acc[char] === undefined) {
acc[char] = 1;
} else {
acc[char] += 1;
}
}
return acc;
}, {});
return Object.keys(charCounts).filter(c => charCounts[c] > 1).length;
}
alert(duplicateCount('aaBbCChr'));

Counter inkrementing

so I just learn how to code in JS, so i just want to warn you that my code may looks very awful for you.
I want a "class" that counts how often a number has been counted.
Example:
counter = new Counter();
counter.count(1);
counter.count(1);
counter.count(3);
Calling the getCounts gives the result:
counter.getCounts();
> 1: 2, 3: 1
My code works, but i have two problems.
Since I store it in an array, I can output the numbers not in order, but in the order they were called.
I don't think it's a pretty solution.
My code:
class Counter {
constructor(arr = []) {
this.arr = arr;
}
count(number) {
var posNumber = [];
var evenNumCheck = false;
var exactPos;
//Check if have the number already in the array
for (var i = 0; i < this.arr.length; i++) {
if (this.arr[i] === number) {
posNumber.push(i);
}
}
//posNumber we have the position of all the numbers that match our argument number
// we check which of them has an even position, only this can be the number we want
// since we save [number,count; number,count]
for (var i = 0; i < posNumber.length; i++) {
if (i % 2 === 0) {
evenNumCheck = true;
exactPos = i;
}
}
if (evenNumCheck) {
this.arr[exactPos + 1]++;
} else {
this.arr.push(number);
this.arr.push(1);
}
}
getCounts() {
var string = '';
for (var i = 0; i < this.arr.length; i += 2) {
if (i + 2 >= this.arr.length) {
string += this.arr[i] + ': ' + this.arr[i + 1];
} else {
string += this.arr[i] + ': ' + this.arr[i + 1] + ', ';
}
}
console.log(string);
}
}
Do you think this solution is ok or are there better solutions for it, which might also output the numbers in order?
The classic way is to use an object as said by #Orkhan Alikhanov
class Counter {
constructor () {
this.dic = {}
}
count (number) {
if (number in this.dic) {
this.dic[number]++
} else {
this.dic[number] = 1
}
}
getCounts () {
// instead of doing the trailing comma stuff
// make an array of string and join the array
const out = Object.entries(this.dic).map(entry => entry.join(':')).join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(1)
counter.count(1)
counter.count(2)
counter.getCounts()
You may prefer using a Map since semantically you just want to map a number to a count.
class Counter {
constructor () {
this.dic = new Map()
}
count (number) {
const count = this.dic.get(number)
this.dic.set(number, count ? count + 1 : 1)
}
getCounts () {
const out = [...this.dic.entries()].map(entry => entry.join(':')).join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(1)
counter.count(1)
counter.count(2)
counter.getCounts()
Finally, if you want the number printed not in the order they were inserted but with the "int" order, you have to sort the entries:
class Counter {
constructor () {
this.dic = new Map()
}
count (number) {
const count = this.dic.get(number)
this.dic.set(number, count ? count + 1: 1)
}
getCounts () {
const out = [...this.dic.entries()]
.sort((a, b) => a[0] - b[0]) // sort by first key (which is the number)
.map(entry => entry.join(':'))
.join(',')
console.log(out)
}
}
const counter = new Counter()
counter.count(2) // insert 2 before
counter.count(1)
counter.count(1)
counter.getCounts()

Try to create a for loop that iteratively adds up the sum of its runs in a total variable

I am trying to write a function in Javascript that will return true or false depending on if the number is an Armstrong number or not. Armstrong numbers are numbers that take each digit of a number and multiple it by the length of the number, then add them all up to equal the original number. For example: 153 is an Armstrong number, because: 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153.
I'm sure my function has other problems and isn't the pretiest but I'm stuck on this.
I've already tried to add parseInt() around multiple areas of the function to ensure that I'm only dealing with integers and not strings without any lucky.
const validate = (num) => {
const numlength = num.toString().length;
let armstrong;
let singleDigit;
for (i = 0; i < numlength; i++) {
singleDigit = num.toString()[i] ** numlength;
armstrong += singleDigit;
}
if (armstrong == num) {
return true;
} else {
return false;
}
};
console.log(validate(153))
The problem that I'm facing is that the armstrong variable is returning NaN for some reason. I'm not quite sure how to iteratively add the singleDigit values that are found in the for statement. When I console.log(singleDigit) right after it is figured out in the for statement, the value is correctly. If I console.log(armstrong) right after that, I get NaN
You need to set initial value of armstrong = 0.
By default it is undefined so when you perform math operation with undefined it results in NaN
const validate = num => {
let numString = num.toString()
const numlength = numString.length;
let armstrong = 0;
let singleDigit = 0;
for (let i = 0; i < numlength; i++) {
singleDigit = numString[i] ** numlength;
armstrong += singleDigit;
}
return armstrong == num
};
console.log(validate(153))
On side note:-
You can simply remove the last if else statement, armstrong == num is already resulting in true or false
Instead of calling toString() on each iteration we can simply store in a variable and use it
One more way is to use reduce instead of for loop
const validate = num => {
let numString = num.toString()
const numLength = numString.length;
const armstrong = [...numString].reduce((acc, r) => acc += r ** numLength, 0)
return armstrong == num
};
console.log(validate(153))

Categories

Resources