I need to find a way of translating a standard arithmetic formula written as a string into another string in the format of a calculation implements BODMAS as a stack of values and operations where each are delimited by a pipes read from left to right.
I do not want the result of the formula, I'm trying to write a javascript function that can be added to an HTML page where a user can enter a formula (example 10 * 6 / 2), have the formula validated, then translated into another formula (result is 10|6|multiply|2|divide). Its translating from one string format to another.
I already have another function that knows how to process formulas written this way, I just need to avoid forcing users to have to write a formula in an unfamiliar way so I need this translation done on the interface.
What I've tried so far is using a split function but I haven't been able to work out how to extend it to create the bodman_value. My javascript skills are basic as. Here's where I got to, any advice of how to approach it appreciated.
const str = '10 * 6 / 2';
const value_1 = str.split(' ');
console.log(value_1[0]);
// expected output: "10"
const operator_1 = str.split(' ');
console.log(operator_1[1]);
// expected output: "*"
const value_2 = str.split(' ');
console.log(value_2[2]);
// expected output: "6"
const operator_2 = str.split(' ');
console.log(operator_2[3]);
// expected output: "/"
const value_3 = str.split(' ');
console.log(value_3[4]);
// expected output: "2"
// expected output: Array ["10","*","6","/", "2"]
// assuming operator always at arroay 'odd' position (strCopy array is 0-4)
// count operators by number of odd positions in array
// then loop to get operator name of each array f_operator
IF strCopy.[i] = "*" THEN f_operator.[i] = "multiply"
IF strCopy.[i] = "+" THEN f_operator.[i] = "add"
IF strCopy.[i] = "-" THEN f_operator.[i] = "subtract"
IF strCopy.[i] = "/" THEN f_operator.[i] = "divide"
var bodman_value
// FOR loop f from 0 to array count
bodman_value = strCopy.[f]] + "|" + strCopy.[f+2] + "|" + operator.[f]
IF array count > 3
bodman_value = bodman_value + "|"
else
Thanks.
If you have the pattern
value [operator, value]+
you could just switch the repeating operator value parts to
value [value, operator]+
var operators = {
'*': 'multiply',
'/': 'divide'
},
string = '10 * 6 / 2',
tokens = string.split(/\s+/),
i = 0,
result = [tokens[i++]];
while (i < tokens.length) {
result.push(tokens[i + 1], operators[tokens[i]]);
i += 2;
}
console.log(result.join('|'));
An even shorter approach with a regular expression and a replacement function.
var operators = {
'*': 'multiply',
'/': 'divide',
'+': 'plus'
},
string = '24 + 6 / 10 * 100',
result = string.replace(/\s+([^\s]+)\s+([^\s]+)/g, (_, o, v) => `|${v}|${operators[o]}`);
console.log(result);
Related
How can I add operation symbols (+, *, -, /) to an array in JavaScript? I am trying to get a randomized symbol from an array and I have 4 options: +, *, -, /.
var operations = [*, +, -, /]
It does not detect it as an array of symbols though.
I am trying to create a webapp for me to practice math. I want to get two random numbers that will get operated (added, multiplied, divided, or subtracted) by a random operation (-, /, *, or +). so the equation becomes totally random, example: 472 * 820 =.
You need quotes around strings.
var operations = ['*', '+', '-', '/'];
var randIdx = Math.random() * operations.length | 0;
var randOp = operations[randIdx];
console.log(randOp);
To actually perform random operations, you do not need the operators themselves: you can just generate a random integer from 0-3 and map each integer to an operation yourself.
var rand1 = Math.random() * 50 | 0 + 50,
rand2 = Math.random() * 50 | 0 + 50,
randOp = Math.random() * 4 | 0;
console.log(rand1, ["+", "-", "*", "/"][randOp], rand2, "=");
switch(randOp){
case 0:
console.log(rand1 + rand2);
break;
case 1:
console.log(rand1 - rand2);
break;
case 2:
console.log(rand1 * rand2);
break;
default:
console.log(rand1 / rand2);
}
The simple answer to "How can I add operation symbols (+, *, -, /) to an array in Javascript" is that you can't do that.
An operator is a symbol in the language, but it is not a string, number, boolean, or an Object, so it can't be part of a data structure.
You can put some kind of representation of an operator in an array, such as a string:
let operations = [ '*', '+', '-', '/' ];
There are several pretty good answers, and with your added explanation of your goal I would recommend something that, in some ways, combines some elements from those answers.
You want to display two random numbers with the choice of a random operation to create a math problem. Displaying them suggests using string representations of the operators.
However, you also want to perform the operation that the operator represents; that means you have to turn that operator string into a computation in some way. One way is to switch on the chosen operator, similar to what hev1 does...
let num1 = 839;
let num2 = 195;
let op = '+';
let answer;
switch (op) {
case '*':
answer = num1 * num2;
break;
case '/':
answer = num1 / num2;
break;
case '+':
answer = num1 + num2;
break;
case '-':
answer = num1 - num2;
break;
}
console.log('answer is', answer);
But now your array and your computation code have to be kept synchronized.
You can also create a function array like 8HoLoN does, but then you have no symbol to display in the page unless you again have a corresponding way to pick both the symbol to display and the operation to perform.
I would create an array of objects, where each object contains the symbol to display and a function to perform the operation.
Look in particular at how the answer is calculated at let answer = ... and how the operator is displayed in the console.log line.
// Here's your array of operators. Each object in the array has
// • an "operator" symbol
// • a calculator to perform that operation.
const operators = [
{
'operator' : '*',
'calc' : (a, b) => a * b
},
{
'operator' : '/',
'calc' : (a, b) => a / b
},
{
'operator' : '+',
'calc' : (a, b) => a + b
},
{
'operator' : '-',
'calc' : (a, b) => a - b
},
];
let num1 = 296;
let num2 = 138;
let op = operators[getRandomIntInclusive(0, operators.length - 1)];
let answer = op.calc(num1, num2);
console.log(num1, op.operator, num2, '=', answer);
/* random int function from MDN
https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Math/random#Getting_a_random_integer_between_two_values_inclusive
*/
function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
You simply need to add quote around the symbols as follows -
var operations = ['*', '+', '-', '/'];
console.log(operations);
function getRandom(){
return operations[Math.floor(Math.random() * Math.floor(4))]
}
// To get a random symbol -
console.log(getRandom())
// One way to use a random symbol in a given expression would be as -
const num1 = Math.floor(100*Math.random()), num2 = Math.floor(100*Math.random());
const expression = `${num1}${getRandom()}${num2}`
const result = eval(expression)
console.log(result)
As you can see, you can use eval(), if you are forming an expression string using the way I have shown in the above code snippet. The eval() function evaluates JavaScript code represented as a string.
You can't. * / + - are operators of the JS language and then you can't reference them as objects in a array.
If what you want is make dynamic random operation, you should use function.
const operators = [
(a, b) => a + b,
(a, b) => a * b,
(a, b) => a - b,
(a, b) => a / b
];
const a = 2, b = 3;
const rand = Math.floor(Math.random() * 3.9);// between [0;4[
const res = operators[rand](a, b);
console.log(res);
If I have a string a12c56a1b5 then out put should be a13b5c56 as character a is repeated twice so a12 becomes a13
I have tried this:
function stringCompression (str) {
var output = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
It is happening because the code is counting the occurrence of each element and appending it, even the numbers in the string.
In this code,
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
in first iteration i = 0, str[i] = 'a' and str[i + 1] = '1' for the given string a12c56a1b5 which are not equal hence, it will generate the output as a1 for first iteration, then a111 for second iteration since str[i] = '1' and str[i + 1] = '2' now, and so on.
We can achieve this by first separating the characters from the count. Assuming, that there would be characters from a-z and A-Z only followed by the count. We can do something like this, str.match(/[a-zA-Z]+/g) to get the characters: ["a", "c", "a", "b"] and str.match(/[0-9]+/g) to get their counts: ["12", "56", "1", "5"], put them in an object one by one and add if it already exists.
Something like this:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
Using regex to extract word characters and numbers. Keeps an object map res to track and sum up following numbers. sorts and converts back to a string.
As an example, the for-of loop iteration flow with str=a12c56a1b5:
c='a', n='12'
res['a'] = (+n = 12) + ( (res['a'] = undefined)||0 = 0)
or ie: res['a'] = 12 + 0
c='c', n='56'
res['c'] = 56 + 0
c='a', n='1'
res['a'] = 1 + (res['a'] = 12 from iteration 1.) = 13
c='b', n='5'
res['b'] = 5 + 0
thus res = { 'a': 13, 'c': 56, 'b': 5 } after the for-of loop finishes
function stringCompression (str) {
// build object map with sums of following numbers
const res = {}
for(const [,c,n] of str.matchAll(/(\w+)(\d+)/g))
res[c] = +n + (res[c]||0)
// convert object map back to string
output = Object.entries(res)
output.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
output = output.map(([a,b])=>`${a}${b}`).join('')
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
stringCompression('a12c56a1b5')
[,c,n] = [1,2,3] is equivalent to c=2, n=3. It is called destructuring.
matchAll matches on a regex. It's a relatively new shorthand for calling .exec repeatedly to execute a regular expression that collects all the results that the regular expression matches on.
(\w+)(\d+) is a regex for two capture groups,
\w+ is for one or more alpha characters, \d+ is for one or more digits.
for(const [,c,n] of str.matchAll...) is equivalent to:
for each M of str.matchAll...
const c = M[1], n = M[2]`
res[c]||0 is shorthand for:
"give me res[c] if it is truthy (not undefined, null or 0), otherwise give me 0"
+n uses the unary operator + to force an implicit conversion to a number. JavaScript specs for + unary makes it convert to number, since + unary only makes sense with numbers.
It is basically the same as using Number(n) to convert a string to an number.
Conversion back to a string:
Object.entries converts an object {"key":value} to an array in the form of [ [key1, value1], [key2, value2] ]. This allows manipulating the elements of an object like an array.
.sort sorts the array. I destructured the keys to sort on the keys, so "a" "b" "c" are kept in order.
.map takes an array, and "maps" it to another array. In this case I've mapped each [key,value] to a string key+value, and then taking the final mapped array of key+value strings and joined them together to get the final output.
In case it asks you to sort it alphabetically, I added #user120242's sorting code snippet to #saheb's entire answer (in between Object.keys(countMap) and .map(...). That worked for me. I tried using #user120242's whole answer, but it did not pass all the tests since it did not add the repeated letters for longer strings. But #user120242's answer did work. It just need to be sorted alphabetically and it passed all the test cases in HackerRank. I had this question for a coding assessment (called "Better Coding Compression").
P.S. I also removed checking the capital letters from #saheb's code since that wasn't required for my coding challenge.
Here's how mine looked like:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
I ran into a bit of a block when wanting to make a dice rolling script.
The idea is that you can input any amount of arguments to roll any amount of dice, 2d6 + 1d4 + 5d2 - [...].
How can I make a script that finds all the xdy's and puts them into an array? It must be based on xdy instead of, for example, spaces for cases like 1d4+1d6-1d8.
You can extract the pairs of numbers with matchAll, like this:
let s = "1d4+1d6-1d8";
let x = Array.from(s.matchAll(/(-?\d+)d(\d+)/g));
console.log(x);
This gives you an array of triplets. Each triplet has the xdy pattern that was matched, the value of x and the value of y (as strings).
If you need the x and y values as numbers instead of strings, then add a mapper callback to the Array.from call:
let s = "1d4+1d6-1d8";
let x = Array.from(s.matchAll(/(-?\d+)d(\d+)/g), ([s, x, y]) => [+x, +y]);
console.log(x);
Start with creating a function that takes a string (like "2d6") and returns a number
function rollDice(dicePattern) {
var num = ... //use regex or indexOf ("d");
var sides = ... //regex or substring(indexOf("d) + 1);
var sum = 0;
for (let i = 0; i <num;i++) {
//generate a random, multiply by sides, get the int value, and add it to sum
}
return sum;
}
Then split the string and call this on all your dice roll patterns
As stated in the comments, regex is your friend. And regexr is super handy! https://regexr.com/4mvj6
\d will match a digit character, so \dd\d will match any number + 'd' + number combinations in your string.
// '2d6 + 1d4 + 5d2' => ['2d6', '1d4', '5d2']
function parseRolls(rollsString)
{
if (rollsString)
{
return rollsString.match(/\dd\d/g);
}
return [];
}
I'm not completely sure what the exact format you're expecting is, but this returns an array of objects with properties x and y given a string of xdys delimited by plusses or minuses.
function parseDiceRoll(rollString) {
let rollBlocks = rollString.split(/[\+\-]/);
return rollBlocks.map(roll => {
let [x, y] = roll.split("d");
return {x, y};
});
}
console.log(parseDiceRoll("3d6+4d8-2d12"));
I want to create a javascript function to flip 1's to 0's in a natural number and I'm out of Ideas to achieve this,
Actually, I had a couple of URL's, and I replaced all 0's from a query parameter with 1's and now I no longer know the original parameter value, because there were few 1's in the original parameter value and now both are mixed, so basically I screwed myself,
The only solution for me is to try flipping each 1 to 0 and then 0's to 1's and test each number as the parameter.
This is the parameter value (after replacing 0's with 1's)
11422971
using above input I want to generate numbers as follows and test each of these
11422970
10422971
10422970
01422971
As you can see only 1's and 0's are changing, the change according to binary,
Each position in your string can be one of n characters:
A "0" can be either "0" or "1"
A "1" can be either "0" or "1"
Any other character c can only be c
We can store this in an array of arrays:
"11422971" -> [ ["0", "1"], ["0, "1"], ["4"], ... ]
To transform your string to this format, you can do a split and map:
const chars = "11422971"
.split("")
.map(c => c === "1" || c === "0" ? ["1", "0"] : [ c ]);
Once you got this format, the remaining logic is to create all possible combinations from this array. There are many ways to do so (search for "array combinations" or "permutations"). I've chosen to show a recursive pattern:
const chars = "11422971"
.split("")
.map(c =>
c === "1" || c === "0"
? ["1", "0"]
: [ c ]
);
const perms = ([ xs, ...others ], s = "", ps = []) =>
xs
? ps.concat(...xs.map(x => perms(others, s + x, ps)))
: ps.concat(s);
console.log(perms(chars));
you can do it with a number like a string, and after parse it, something like that
var number= "12551";
number= number.replace("1","0");
The result of number will be "02550"
after that parse number to int
This will generate all permutations.
const generatePermutations = (number) => {
let digits = number.split('');
// find out which digits can be flipped
let digitPositions = digits.reduce((acc, val, i) => {
if (val === '0' || val === '1') acc.push(i);
return acc;
}, []);
// we're going to be taking things in reverse order
digitPositions.reverse();
// how many digits can we flip
let noBits = digitPositions.length;
// number of permutations is 2^noBits i.e. 3 digits means 2^3 = 8 permutations.
let combinations = Math.pow(2, digitPositions.length);
let permutations = [];
// for each permutation
for (var p = 0; p < combinations; p++) {
// take a copy of digits for this permutation
permutations[p] = digits.slice();
// set each of the flippable bits according to the bit positions for this permutation
// i = 3 = 011 in binary
for (var i = 0; i < noBits; i++) {
permutations[p][digitPositions[i]] = '' + ((p >> i) & 1);
}
permutations[p] = permutations[p].join('');
}
return permutations;
};
console.log(generatePermutations('11422970'));
In case your looking for a recursive approach:
function recursive(acc, first, ...rest) {
if(!first) return acc;
if(first == '0' || first == '1') {
var acc0 = acc.map(x => x + '0');
var acc1 = acc.map(x => x + '1');
return recursive([].concat(acc0, acc1), ...rest);
} else {
return recursive(acc.map(x => x + first), ...rest);
}
}
recursive([''], ...'11422971')
// output ["00422970", "10422970", "01422970", "11422970", "00422971", "10422971", "01422971", "11422971"]
This just counts in binary and fills out a template for each value.
function getPossibleValues(str) {
function getResult(n) {
let nIndex = 0;
const strValue = n.toString(2).padStart(nDigits, '0');
return str.replace(rxMatch, () => strValue.charAt(nIndex++));
}
const rxMatch = /[01]/g;
const nDigits = str.length - str.replace(rxMatch, '').length;
const nMax = Math.pow(2, nDigits);
const arrResult = [];
for(let n = 0; n<nMax; n++) {
arrResult.push(getResult(n));
}
return arrResult;
}
console.log(getPossibleValues('11422970'));
Thank you all to respond, you saved my life, btw the approach I used was,
0- convert the number into a string. (so we can perform string operations like split())
1- count the number of 1's in the string (let's say the string is "11422971", so we get three 1's, I used split('1')-1 to count)
2- generate binary of three-digit length,(ie from 000 to 111). three came from step 1.
2- break down the string to single chars, (we'll get
array=['1','1','4','2','2','9','7','1'] )
3- take the first binary number (ie b=0b000)
4- replace first 1 from the character array with the first binary digit of b (ie replace 1 with 0), similarly replace second 1 with the second binary digit of b and so on.
5- we'll get the first combination (ie "00422970")
5- repeat step 3 and 4 for all binary numbers we generated in step 2.
I want to Split a number into its digit (for example 4563 to 4 , 5 , 6 , 3 ) then addiction this digits. (for example: 4+5+6+3=18)
I can write code for 3 digit or 2 digit and ... numbers seperately but I cant write a global code for each number.
so this is my code for 2 digit numbers:
var a = 23
var b = Math.floor(a/10); // 2
var c = a-b*10; // 3
var total = b+c; // 2+3
console.log(total); // 5
and this is my code for 3 digit numbers:
var a = 456
var b = Math.floor(a/100); // 4
var c = a-b*100; // 56
var d = Math.floor(c/10); // 5
var e = c-d*10; // 6
var total = b+d+e; // 4+5+6
console.log(total); // 15
but I cant write a code to work with each number.How can I write a global code for each number?
In modern browsers you can do an array operation like
var num = 4563;
var sum = ('' + num).split('').reduce(function (sum, val) {
return sum + +val
}, 0)
Demo: Fiddle
where you first create an array digits then use reduce to sum up the values in the array
var num = 4563;
var sum = 0;
while(num > 0) {
sum += num % 10;
num = Math.floor(num / 10);
}
console.log(sum);
Do number%10(modulus) and then number/10(divide) till the number is not 0
I hope the following example is useful to you:
var text="12345";
var total=0;
for (i=0;i<text.length;i++)
{
total+= parseInt(text[i]);
}
alert(total);
This solution converts the number to string, splits it into characters and process them in the callback function (prev is the result from the previous call, current is the current element):
var a = 456;
var sum = a.toString().split("").reduce(function(prev, current){
return parseInt(prev) + parseInt(current)
})
Here is how I would approach the problem. The trick I used was to split on the empty string to convert the string to an array and then use reduce on the array.
function digitSum(n) {
// changes the number to a string and splits it into an array
return n.toString().split('').reduce(function(result, b){
return result + parseInt(b);
}, 0);
}
As mentioned by several other posters (hat tip to my commenter), there are several other good answers to this question as well.
Here is my solution using ES6 arrow functions as call back.
- Convert the number into a string.
- Split the string into an array.
- Call the map method on that array.
- Callback function parse each digit to an array.
let number = 65535;
//USING MAP TO RETURN AN ARRAY TO DIGITS
let digits = number.toString()
.split("")
.map(num => parseInt(num));
//OUTPUT TO DOM
digits.forEach(
digit =>
document.querySelector("#out").innerHTML += digit + "<br>"
);
<p id="out"></p>
1) You can cast input number to string, using .toString() method and expand it into array with spread (...) operator
const splitNumber = n => [ ...n.toString() ]
2) Another short way - using recursion-based solution like:
const splitNumber = n => n ? [ ...splitNumber(n/10|0), n%10 ] : []