Enabling multiple operations in a calculator via vanilla js - javascript

I'm trying to practice my js and I'm doing a calculator project. I've got it to work but I can only do one calculation. I'm stuck and I need to be able to carry out multiple operations. Please note I'm not yet done with the project and so i'm testing for functionality, also some buttons are yet to be worked on. I've also been advised not to use eval() method.
Here is what I need help with:
** Users should be able to string together several operations and get the right answer, with each pair of numbers being evaluated at a time. For example, 12 + 7 - 5 * 3 = should yield 42.**
let add = function(a,b) {
return a + b;
}
let subtract = function(a,b) {
return a - b;
}
let multiply = function(a,b) {
return a * b;
}
let divide = function(a,b) {
return a / b;
}
let operator = function(operate,a,b) {
if (operate === '+') {
return add(a,b);
} else if (operate === '-') {
return subtract(a,b);
} else if (operate === '*') {
return multiply(a,b);
} else if (operate === '/') {
return divide(a,b);
}
}
let screen = document.querySelector('div');
let buttons = document.querySelector('.btns');
let data = ''
let numOne = ''
let numTwo = ''
let result = ''
let operatorSymbol = ''
buttons.addEventListener('click', e => {
data += e.target.value;
screen.textContent = data;
if ((e.target.value === '+') || (e.target.value === '-') || (e.target.value === '*') ||
(e.target.value === '/')){
numOne = data.slice(0,-1);
operatorSymbol += e.target.value;
}
numTwo = data.slice(-((data.length-1) - numOne.length));
if(e.target.value === '='){
data=data.slice(0,-1);
numTwo=numTwo.slice(0,-1);
result=operator(operatorSymbol,+numOne,+numTwo);
data = result;
screen.textContent = data;
}
})
The full webpage can be found here : https://repl.it/#socman/SweetZealousLearning#app.js
Any help will be appreciated. Thanks!

So basically you end up with a string like "12+7-5*3". One option is to use regex to parse the string and do the math on the resulting array. This is not the only way, but for simple equations like the ones you have, it should work.
// Perform math operation on 2 values
function doMath(op1, op, op2)
{
if (op === '+') return op1 + op2;
if (op === '-') return op1 - op2;
if (op === '*') return op1 * op2;
if (op === '/') return op1 / op2;
return 0; // error
}
// Test string
const eq = "12+7-5*3";
// Parse with regex
const regex = /\d+|[-+\*\/]/g;
const parsed = eq.match(regex);
// Store first number (12 in the example)
if (parsed && parsed.length && !isNaN(parsed[0])) {
let result = Number(parsed[0]);
for (let i = 1; i < eq.length - 1; i++) {
// Do the math with previous result and next number
if ("+-*/".includes(parsed[i]) && !isNaN(parsed[i+1])) {
result = doMath(result, parsed[i], Number(parsed[i+1]));
}
}
console.log(result);
}

Related

Beginner question: returning number value from string

If I give a value to firstParam as 'Ten' or '10' is there some way I can make the program return a number value to the function?
const sum = (firstParam, secondParam) => {
if (firstParam === Number(firstParam) && secondParam === Number(secondParam))
return (firstParam - secondParam)
else
return ('Invalid argument(s)')
}
console.log(sum(1,1))
EDIT
I have tried many different ways changing the firstParam to Number.firstParam, when i get close i miss out on the correct return statements
const sum = (firstParam, secondParam) =>{
firstParam = Number.firstParam
if (firstParam === Number(firstParam) && secondParam === Number(secondParam) )
return (firstParam - secondParam);
else
return ('Invalid argument(s)')
}
console.log(sum(1,1))
You need to check the type of the arguments before trying to convert them to a number:
const sum = (firstParam, secondParam) => {
// If both are numbers everything is ok
if (typeof firstParam === 'number' && typeof secondParam === 'number' ) {
return (firstParam - secondParam);
}
// If are stingified numebrs we convert them
const firstParamInNumber = parseFloat(firstParam)
const secondParamInNumber = parseFloat(secondParam)
return isNaN(firstParamInNumber) || isNaN(secondParamInNumber) ? 'Invalid argument(s)' : firstParamInNumber - secondParamInNumber
}
console.log(sum(1,'1'))
console.log(sum(1,1))
console.log(sum(1,'wrong param'))
If you provide a number in form of string. for example '10'.
You can parse it into Number and then return the sum as shown below.
Example when both arguments are correct.
const sum = (firstParam, secondParam) =>{
let sum = Number(firstParam)+Number(secondParam);
return Number(sum)?sum:"Invalid argument(s)";
}
console.log(sum('10','1'))
Example when anyone of the argument is incorrect.
const sum = (firstParam, secondParam) =>{
let sum = Number(firstParam)+Number(secondParam);
return Number(sum)?sum:"Invalid argument(s)";
}
console.log(sum('ten','1'))

Return the days of the week concatenated when provided a string of consecutive days of the week

I need of a function that can take a string of days like "Mon,Tues,Wed,Thurs,Fri" and return "Mon-Fri" Of course, it cannot simply return first and last concatenated with "-" but should account for "Mon,Tues,Thurs,Fri" in that case could either return the input string OR "Mon-Tues, Thurs-Fri"
Virtual cheers to whoever can help out!
function daysConcat(days) {
let key = ['Sun','Mon','Tues','Wed','Thu','Fri','Sat'];
let daysArr = days.split(',');
let consecutive = true;
let lastIndex = '';
let concatDays = daysArr.filter((day, i, arr) => {
if (day === arr[arr.length-1] && consecutive && key.indexOf(day) === lastIndex+1) {
return day;
} else if (day === arr[0]) {
lastIndex = key.indexOf(day);
return day;
} else if (key.indexOf(day) === lastIndex+1) {
lastIndex = key.indexOf(day);
} else {
consecutive = false;
}
})
if (concatDays.length > 1) {
return `${concatDays[0]} - ${concatDays[concatDays.length-1]}`
} else {
return days
}
}
let consecutiveA = 'Mon,Tues,Wed,Thu,Fri';
console.log(daysConcat(consecutiveA));
let consecutiveB = 'Mon,Tues,Wed,Thu';
console.log(daysConcat(consecutiveB));
let notConsecutiveA = 'Mon,Tues,Thu,Fri';
console.log(daysConcat(notConsecutiveA));
let notConsecutiveB = 'Sun,Mon,Tues,Thu,Fri';
console.log(daysConcat(notConsecutiveB));
Currently the above works according to my first "ask", though I would love a more elegant/straightforward approach. Also it will of course not produce "Mon-Tues, Thurs-Fri" for input string "Mon,Tues,Thurs,Fri" - any thoughts or improvements appreciated!

Get binary representation of integer

I just had an interview question, where I need to get the binary representation of an integer, this is something I should know how to do.. for example, 5 is represented in binary as 101, and the steps look something like:
// 5 % 2 = 1
// 5 / 2 = 2
// result = 1;
// 2 % 2 = 0
// 2 / 2 = 1
// result = 10
// 1 % 2 = 1
// 1 / 2 = 0
// result = 101
the stopping condition is when ~~(1/2) === 0
so I have this:
const getBinary = (v) => {
let remainder, binary = 1;
while (true) {
remainder = v % 2;
v = ~~(v / 2);
if (v === 0) {
return binary;
}
if (remainder === 0) {
binary = binary * 10 + 1;
}
else {
binary = binary * 10;
}
}
};
console.log(getBinary(5));
so that works, but the binary variable is initialized to 1. Is there a way to improve this so it works with negative numbers, or if 0 is passed as the argument to the function?
var integer = 52;
console.log(integer.toString(2));
Simple function native to javascript, no lengthy code required.
If you want to write it from scratch you can use something like this:
function toBinary(n) {
n = Number(n);
if (n == 0) return '0';
var r = '';
while (n != 0) {
r = ((n&1)?'1':'0') + r;
n = n >>> 1;
}
return r;
}
console.log(toBinary(5));
console.log(toBinary(10));
console.log(toBinary(-5));
console.log(toBinary(0));
So here is one way. It has an inner function that handles the basics and an outer one that extends to your special cases. I preferred to do string representations.
const getBinary = v => {
if (v === 0) return '';
let remainder = v % 2;
let quotient = (v - remainder) / 2;
if (remainder === 0) {
return getBinary(quotient) + '0';
}
else {
return getBinary(quotient) + '1';
}
}
const betterGetBinary = v => {
if (v === 0) return '0';
if (v < 0) return '-' + getBinary(-v);
return getBinary(v);
}
console.log(betterGetBinary(-10));
A quick and dirty solution, although it 'might' have two flaws:
- Math.floor()
- no bitwise operator
let getBinary = number => {
let done = false;
let resultInverted = [];
let acc = number;
while (!done) {
let reminder = acc % 2;
if (acc === 1) {
done = true;
}
acc = Math.floor(acc / 2);
resultInverted.push(reminder);
}
return Number(resultInverted.reverse().join(''));
};
console.log(typeof getBinary(2));
console.log(getBinary(5));
console.log(getBinary(127));

type conversion not working javascript

I created a rather large function that takes in an argument, and based on the number's size, properly formats it. It works as expected for any values with the typeof number, however there are a few instances where I need to convert a string to a numeric value. I am trying to do that by using parseInt if its type is not number. When I console.log after the first if-statement, it says its typeof is now number. However when any string is passed through, say, "10000", it just shows up as 10000, and not the expected output of 10,000 with the proper formatting. Below is my code...are there any glaring ways that I might be going about it wrong?
function formatNumber(number) {
if (typeof number !== 'number') {
number = parseInt(number);
return number;
}
let decimals = 2;
const isNegative = number < 0;
const rounded = (number >= 1e+6) ? Math.round(number) : number;
const isInteger = () => parseInt(number, 10) === parseFloat(number);
const abs = Math.abs(rounded);
if (abs < 1e+6) {
decimals = isInteger(abs) ? 0 : decimals;
}
const formatter = val => {
const string = Math.abs(val).toString();
let formatted = '';
for (let i = string.length - 1, d = 1; i >= 0; i--, d++) {
formatted = string[i] + formatted;
if (i > 0 && d % 3 === 0) {
formatted = `,${formatted}`;
}
}
return formatted;
};
const formatLargeNumbers = (value, decimalPlaces) => {
let adjustedValue;
if (value >= 1e+12) {
adjustedValue = parseFloat((value / 1e+12).toFixed(decimals));
return `<span title=${formatter(value)}>${adjustedValue}T</span>`;
}
if (value >= 1e+9) {
adjustedValue = parseFloat((value / 1e+9).toFixed(decimals));
return `<span title=${formatter(value)}>${adjustedValue}B</span>`;
}
if (value >= 1e+6) {
adjustedValue = parseFloat((value / 1e+6).toFixed(decimals));
return `<span title=${formatter(value)}>${adjustedValue}M</span>`;
}
if (value >= 1e+3 && value < 1e+6) {
return value.toLocaleString('en-EN', {
minimumFractionDigits: decimalPlaces,
maximumFractionDigits: decimalPlaces,
});
}
return value.toFixed(decimals);
};
if (isNegative) {
let negativeNumber = Math.abs(rounded);
if (negativeNumber >= 1e+3) {
negativeNumber = formatLargeNumbers(negativeNumber);
}
return `<span class="negative-value">(${negativeNumber})</span>`;
}
return formatLargeNumbers(rounded, decimals);
}

Get function parameter length including default params

If you make use of the Function.length property, you get the total amount of arguments that function expects.
However, according to the documentation (as well as actually trying it out), it does not include Default parameters in the count.
This number excludes the rest parameter and only includes parameters before the first one with a default value
- Function.length
Is it possible for me to somehow get a count (from outside the function) which includes Default parameters as well?
Maybe you can parse it yourself, something like:
function getNumArguments(func) {
var s = func.toString();
var index1 = s.indexOf('(');
var index2 = s.indexOf(')');
return s.substr(index1 + 1, index2 - index1 - 1).split(',').length;
}
console.log(getNumArguments(function(param1, param3 = 'test', ...param2) {})); //3
Copying my answer over to here from a duplicate question:
Well, it's a bit of a mess but I believe this should cover most edge cases.
It works by converting the function to a string and counting the commas, but ignoring commas that are in strings, in function calls, or in objects/arrays. I can't think of any scenarios where this won't return the proper amount, but I'm sure there is one, so this is in no way foolproof, but should work in most cases.
UPDATE: It's been pointed out to me that this won't work for cases such as getNumArgs(a => {}) or getNumArgs(function(a){}.bind(null)), so be aware of that if you try to use this.
function getNumArgs(func) {
var funcStr = func.toString();
var commaCount = 0;
var bracketCount = 0;
var lastParen = 0;
var inStrSingle = false;
var inStrDouble = false;
for (var i = 0; i < funcStr.length; i++) {
if (['(', '[', '{'].includes(funcStr[i]) && !inStrSingle && !inStrDouble) {
bracketCount++;
lastParen = i;
} else if ([')', ']', '}'].includes(funcStr[i]) && !inStrSingle && !inStrDouble) {
bracketCount--;
if (bracketCount < 1) {
break;
}
} else if (funcStr[i] === "'" && !inStrDouble && funcStr[i - 1] !== '\\') {
inStrSingle = !inStrSingle;
} else if (funcStr[i] === '"' && !inStrSingle && funcStr[i - 1] !== '\\') {
inStrDouble = !inStrDouble;
} else if (funcStr[i] === ',' && bracketCount === 1 && !inStrSingle && !inStrDouble) {
commaCount++;
}
}
// Handle no arguments (last opening parenthesis to the last closing one is empty)
if (commaCount === 0 && funcStr.substring(lastParen + 1, i).trim().length === 0) {
return 0;
}
return commaCount + 1;
}
Here are a few tests I tried it on: https://jsfiddle.net/ekzuvL0c/
Here is a function to retrieve the 'length' of a function (expression or object) or an arrow function expression (afe). It uses a regular expression to extract the arguments part from the stringified function/afe (the part between () or before =>) and a regular expression to cleanup default values that are strings. After the cleanups, it counts the comma's, depending on the brackets within the arguments string.
Note This will always be an approximation. There are edge cases that won't be covered. See the tests in this Stackblitz snippet
const determineFnLength = fnLenFactory();
console.log(`fnTest.length: ${determineFnLength(fnTest)}`);
function fnTest(a,
b,
c = 'with escaped \' quote and, comma',
d = "and double \" quotes, too!" ) { console.log(`test123`); }
function fnLenFactory() {
const fnExtractArgsRE = /(^[a-z_](?=(=>|=>{)))|((^\([^)].+\)|\(\))(?=(=>|{)))/g;
const valueParamsCleanupRE = /(?<=[`"'])([^\`,].+?)(?=[`"'])/g;
const countArgumentsByBrackets = params => {
let [commaCount, bracketCount, bOpen, bClose] = [0, 0, [...`([{`], [...`)]}`]];
[...params].forEach( chr => {
bracketCount += bOpen.includes(chr) ? 1 : bClose.includes(chr) ? -1 : 0;
commaCount += chr === ',' && bracketCount === 1 ? 1 : 0; } );
return commaCount + 1; };
const extractArgumentsPartFromFunction = fn => {
let fnStr = fn.toString().replace(RegExp(`\\s|function|${fn.name}`, `g`), ``);
fnStr = (fnStr.match(fnExtractArgsRE) || [fn])[0]
.replace(valueParamsCleanupRE, ``);
return !fnStr.startsWith(`(`) ? `(${fnStr})` : fnStr; };
return (func, forTest = false) => {
const params = extractArgumentsPartFromFunction(func);
const nParams = params === `()` ? 0 : countArgumentsByBrackets(params);
return forTest ? [params, nParams] : nParams;
};
}

Categories

Resources