Randomization Display - javascript

I have a randomizer that gives me 6 different letters and 6 different numbers. This is going to, in the end, be used for a hex number.
I am trying to create a randomizer that will choose between the number or the letter for each slots of the hex (1-6).
I can create one that chooses in between them, but once it chooses, it doesn't know what they number to letter transition was
Example:
if (mega === 1) {
return "a";
}
It does not remember that transition once inside of the letter/number chooser.
If anyone understands what I'm getting at, please help! Thank you. :)
//edit -- entire code
`
Variables Below Here
//Numbers
var ayy2 = Math.random() * 10;
var ayy = Math.floor(ayy2);
var lmd2 = Math.random() * 10;
var lmd = Math.floor(lmd2);
var qua2 = Math.random() * 10;
var qua = Math.floor(qua2);
var abc2 = Math.random() * 10;
var abc = Math.floor(abc2);
var cdf2 = Math.random() * 10;
var cdf = Math.floor(cdf2);
var lgt2 = Math.random() * 10;
var lgt = Math.floor(lgt2);
//Letters
var mega2 = Math.random() * 7;
var mega = Math.floor(mega2);
var nade2 = Math.random() * 7;
var nade = Math.floor(nade2);
var shot2 = Math.random() * 7;
var shot = Math.floor(shot2);
var suck2 = Math.random() * 7;
var suck = Math.floor(suck2);
var pepe2 = Math.random() * 7;
var pepe = Math.floor(pepe2);
var lols2 = Math.random() * 7;
var lols = Math.floor(lols2);
//Choices Between Hexes\\
var shrek2 = Math.random() * 3;
var shrek = Math.floor(shrek2);
var tpkek2 = Math.random() * 3;
var tpkek = Math.floor(tpkek2);
var waffl2 = Math.random() * 3;
var waffl = Math.floor(waffl2);
var snipe2 = Math.random() * 3;
var snipe = Math.floor(snipe2);
var optic2 = Math.random() * 3;
var optic = Math.floor(optic2);
var fazed2 = Math.random() * 3;
var faze = Math.floor(fazed2);
// Just for Dylan: ff88c6
//Functions Below Here\\
//Letter Returns
function returnLetter1() {
if (mega === 1) {
return "a";
}
if (mega === 2) {
return "b";
}
if (mega === 3) {
return "c";
}
if (mega === 4) {
return "d";
}
if (mega === 5) {
return "e";
}
if (mega === 6) {
return "f";
} else {
return "f";
}
}
function returnLetter2() {
if (nade === 1) {
return "a";
}
if (nade === 2) {
return "b";
}
if (nade === 3) {
return "c";
}
if (nade === 4) {
return "d";
}
if (nade === 5) {
return "e";
}
if (nade === 6) {
return "f";
} else {
return "f";
}
}
function returnLetter3() {
if (shot === 1) {
return "a";
}
if (shot === 2) {
return "b";
}
if (shot === 3) {
return "c";
}
if (shot === 4) {
return "d";
}
if (shot === 5) {
return "e";
}
if (shot === 6) {
return "f";
} else {
return "f";
}
}
function returnLetter4() {
if (suck === 1) {
return "a";
}
if (suck === 2) {
return "b";
}
if (suck === 3) {
return "c";
}
if (suck === 4) {
return "d";
}
if (suck === 5) {
return "e";
}
if (suck === 6) {
return "f";
} else {
return "f";
}
}
function returnLetter5() {
if (pepe === 1) {
return "a";
}
if (pepe === 2) {
return "b";
}
if (pepe === 3) {
return "c";
}
if (pepe === 4) {
return "d";
}
if (pepe === 5) {
return "e";
}
if (pepe === 6) {
return "f";
} else {
return "f";
}
}
function returnLetter6() {
if (lols === 1) {
return "a";
}
if (lols === 2) {
return "b";
}
if (lols === 3) {
return "c";
}
if (lols === 4) {
return "d";
}
if (lols === 5) {
return "e";
}
if (lols === 6) {
return "f";
} else {
return "f";
}
}
//Return Numbers
function returnNumber1() {
var zz = qua;
return zz;
}
function returnNumber2() {
var mm = lmd;
return mm;
}
function returnNumber3() {
var hh = ayy;
return hh;
}
function returnNumber4() {
var jj = abc;
return jj;
}
function returnNumber5() {
var ww = cdf;
return ww;
}
function returnNumber6() {
var rr = lgt;
return rr;
}
/*
This next little bit here is for my enjoyment and because it's necessary for the script. :P
*/
//Obviously, the above are to get a number of 1 or 2 (0 being else so 66/33% chances w/e) to find out if it will be a number or letter.
//But, I can't find a way for the function below to show me what the hexes will be WITH the number>letter from the returnL's.
//Could it be that I possibly would have to move the mega === x below the hexchoices? sigh
function returnHexChoice1() {
if (shrek === 1) {
return zz;
} else {
return mega;
}
}
//Finals of the script display necessary.
//DID I WIN???????
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ )
{
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function hex() {
return "philosophy";
}

Wow, you are pretty impressive with variable naming. I usually name mines "fart" or something like that. That is also because if you try to make use of constructs of a programming language, you will not need as many variables.
Before you ask any other questions, tou might want to check out a tutorial. If javascript is what is interesting you, try here : for example here : http://www.htmldog.com/guides/javascript/. Learn about functions, arrays, etc.
For example, all your returnLetterX() are the same except they use a different value. Try making just one returnLetter(value) that uses value to know what to return. Also, instead of a long set of if/else statements on the same variable, you can use a switch. That's exactly what they're made for !
Then your returnNumberX functions don't do anything, except map from your names to the number. You might want to use arrays, so you have less names.
Finally, all your rand() calls at the start are repetitive as well. Combined with arrays, you can easily do this in a loop.
Your code then could look something like :
function letter(let)
{
switch(let)
{
case 0: return 'a';
case 1: return 'b';
case 2: return 'c';
case 3: return 'd';
case 4: return 'e';
case 5: return 'f';
}
// even better, use an array :
// var abcdef = ['a', 'b', 'c', 'd', 'e', 'f'];
// return abcdef[let];
}
function hexChoice(num, let, choice)
{
if (choice == 0) // 0 for 33%, 1-2 for 67%
return letter(let);
else
return num;
}
// this is not the best way to pick hex values !
for(var i=0; i<6; i++)
{
var number = Math.floor( Math.random() * 10 );
var letter = Math.floor( Math.random() * 6 ); // there are only 6 letters !
var letnum = Math.floor( Math.random() * 3 );
var hex_digit = hexChoice(number, letter, letnum);
// now do something with your hex digit... print it, add it to the "color" string ?
}
You can see it's much smaller, and much easier to read for a programmer.
Once you understand all this code and you are capable to write something similar by yourself you can wonder what is the best way to pick a a random hex digit, though it already is in the getRandomColor you copied from somewhere. Search the internet (this site, tutorials...) it is a question that is asked a lot already and does not need re-asking.

Related

number to grade letter

As a recent assignment for my coding bootcamp, we've been asked to create a function that takes an array of numbers as an argument and outputs them to an array of letter grades. I am stuck!
I've tried re-working and refactoring my code, changing the placement of different parts of the program, looking through MDN...
let grades = []
function getLetterGrades(grades) {
let grade = grades.map
if (grade < 60) {
return "F";
} else if (grade < 70) {
return "D";
} else if (grade < 80) {
return "C";
} else if (grade < 90) {
return "B";
} else if (grade < 100) {
return "A";
}
console.log(grades);
}
getLetterGrades([95, 85, 71]);
The results will only output the numbers I've entered into the function call.
You are using .map() wrong. What you are doing is comparing the map method to a number. You are not executing anything.
function getLetterGrades(grades) {
return grades.map(function(grade) {
if (grade < 60) {
return "F";
} else if (grade < 70) {
return "D";
} else if (grade < 80) {
return "C";
} else if (grade < 90) {
return "B";
} else if (grade < 100) {
return "A";
}
});
}
var letters = getLetterGrades([95, 85, 71]);
console.log(letters)
Your main issue is this:
let grade = grades.map
You are not invoking the .map method with (), so instead grade is winding up holding a reference to the native map function. And, that function isn't a number, so none of your conditions become true, so you continue past the if statement and just log the array that you passed in.
Instead, you must invoke .map() and supply its required parameter (a function that will be called for each item in the source array). Your if statement should be the body of that function:
let grades = []
function getLetterGrades(grades) {
let letterGrades = grades.map(function(grade){
if (grade < 60) {
return "F";
} else if (grade < 70) {
return "D";
} else if (grade < 80) {
return "C";
} else if (grade < 90) {
return "B";
} else if (grade < 100) {
return "A";
}
});
console.log(letterGrades);
}
getLetterGrades([95, 85, 71]);
Look at this solution:
let grades = []
function getLetterGrades(grades) {
// add an array (grade) that will hold the output
let grade = []
// iterate over grades with forEach()
grades.forEach(item => {
// item will be equal 95 on the first iteration
// 85 on the second, and 71 on the third - these
// values come from the passed 'grades' parameter
if (item < 60) {
grade.push("F");
} else if (item < 70) {
grade.push("D");
} else if (item < 80) {
grade.push("C");
} else if (item < 90) {
grade.push("B");
} else if (item < 100) {
grade.push("A");
}
})
// console.log(grade) - NOT grades!
console.log(grade);
}
getLetterGrades([95, 85, 71]);
The problem was not with the method you chose - the problem was you didn't finish your function. Here's another solution with map():
let grades = []
function getLetterGrades(grades) {
let grade = grades.map(item => {
if (item < 60) {
return "F";
} else if (item < 70) {
return "D";
} else if (item < 80) {
return "C";
} else if (item < 90) {
return "B";
} else if (item < 100) {
return "A";
}
})
// console.log(grade) - NOT grades!
console.log(grade);
}
getLetterGrades([95, 85, 71]);
In this case the main difference between forEach() and map() is that map() returns a NEW array (that's why you return values in the function body), and forEach() doesn't (we had to create the array - grade- manually, and push values into this "hand-made" array).
Look below to see what would happen, if we used forEach() WITHOUT a manually created array:
// THIS IS NOT A GOOD SOLUTION!
// IT GIVES YOU THE ANSWER IN THIS SMALL EXAMPLE
// (so you see that it's possible)
// BUT IN ANY LARGER CODE THIS IS THE
// 100% SURE SOURCE OF ERRORS.
let grades = []
function getLetterGrades(grades) {
grades.forEach((item, index) => {
if (item < 60) {
grades[index] = "F";
} else if (item < 70) {
grades[index] = "D";
} else if (item < 80) {
grades[index] = "C";
} else if (item < 90) {
grades[index] = "B";
} else if (item < 100) {
grades[index] = "A";
}
})
// console.log(grades) - NOT grade!
console.log(grades);
}
getLetterGrades([95, 85, 71]);
(I used the second argument of forEach() - that's index) THIS IS NOT A GOOD SOLUTION! Why? We "destroyed" our original grades array by overwriting it in getLetterGrades() - DON'T DO THIS!
As stated on map() takes a function and runs it once on each object in the array. To be clear on how you're trying to use it:
function getLetterGrades(grade) {
if (grade < 60) {
return "F";
} else if (grade < 70) {
return "D";
} else if (grade < 80) {
return "C";
} else if (grade < 90) {
return "B";
} else if (grade < 100) {
return "A";
}
}
x = [95, 85, 71];
//=> [A, B, C]
x.map(getLetterGrades);
Functionally, this is what the other answers are doing, they just aren't naming the method.
Post edited to change link as comment pointed out better resource.

Replace a part of a string with units of a number

I want to implement a function in Google Spreadsheet using JavaScript. I have:
var s1 = "000000000000"; // (12 zeros)
var n1 = 1;
I have a third number between 1 and 12. If my number is 6 the result would need to be: 111111000000. If my number is 8 the result would need to be 111111110000.
So far I got stuck here:
function REP(b1) {
var s1 = "000000000000" ;
var n1 = 1;
if (b1 >=1 && b1 <= 12) {
return s1.split("0").join(n1) ;
}
else{
return "no value" ;
}
}
I prefer a simple approach:
function rep(b1) {
var s1 = "000000000000";
var s2 = "111111111111";
if (b1 >=1 && b1 <= 12) {
return s2.slice(0, b1) + s1.slice(b1);
} else {
return "no value";
}
}
console.log(rep(6)); // 111111000000
console.log(rep(8)); // 111111110000
const replacement = "1"
function replaceZeros(n) {
var result = "000000000000"
while (n --> 0) { // it actually mean `n-- > 0`, but it looks cooler that way
result = result.replace(/0/, replacement)
}
return result
}
This simply replaces the first 0 in the string n times.
See demo on JS Bin.
interesting. i would try
function REP(b1) {
var ret = ""
for (var i = 0; i < 12; i++){
if(i < b1){
ret += "1";
}
else{
ret += "0";
}
}
return ret;
}
hrm... there might be a nicer way though...
EDIT: oh yeah, i think you can do:
function REP(b1) {
if(isNaN(b1) || b1 < 1 || b1 > 12){
return 'no answer';
}
return "1".repeat(b1) + "0".repeat(12-b1);
}
you can use Array.join to repeat characters
see following example...
alert(REP(6));
function REP(b1) {
var s1 = "000000000000";
var n1 = 1;
if (b1 >=1 && b1 <= 12) {
return (Array(b1 + 1).join(n1) + s1).substr(0, 12);
} else{
return "no value" ;
}
}
Not sure how performant this is but here's a regex method.
function REP(b1) {
if (b1 < 1 || b1 > 12) return 'no value';
var rx = new RegExp('0{' + b1 + '}');
return '000000000000'.replace(rx, Array(b1 + 1).join(1));
}
DEMO

how do I loop back to the previous functions when stop is only prompted at the

I am facing a problem to create a loop which will loop back to the previous functions when the user does not want the program to stop (if the user wants it to stop, the program will continue with other functions).
I need to create a list of functions to do base conversion while showing the logic:
Step1: prompt for a number
Step2: prompt for an alphabet (b for Binary/o for Octal/h for Hexadecimal) as the base
Step3: convert it to a string (e.g. "108sup10 = 1101100sup2" & "63300268sup10 = 3C5E2A7sup16")
Step4: alert the string answer in a statement (e.g: Base 10 number 63300268 is 3C5E2A7 in Hexadecimal)
Step5: prompt to stop. If user's input is not "s", it will repeat step 1~4, else it continue to step 6.
Step 6: alert the max and min number entered from (repeated) step1's input.
for step 1,2,3,4,6, it is mandatory to use functions.
May I know how do I code for STEP5 in order to loop back from step 1-4 when stopping is prompted? Do I need a function for this?
//prompt to get number
function getNumber() {
var myNumber;
do {
myNumber = Number(prompt("Enter an unsigned base 10 number:")); //prompt user's input to be excecuted first
} while (myNumber < 0) //loop will run again and again as long as the number is less than zero
return myNumber;
}
//prompt to get base
function getBase() {
var myBase;
do {
myBase = (prompt("Enter b for binary, o for octal and h for hexadecimal"));
} while (!(myBase == "b" || myBase == "B" || myBase == "s" || myBase == "S"|| myBase == "h" || myBase == "H")) //loop if the input is not b, s or h
return myBase;
}
//converting the base to the number
function baseConversion(number, newBase) {
var arr = [];
if (newBase == "b" || newBase == "B") {
newBase = 2;
} else if (newBase == "o" || newBase == "O") {
newBase = 8;
}else if (newBase == "h" || newBase == "H") {
newBase = 16;
}
do { //putting the each remainder at the front of the array
arr.unshift(number%newBase);
number = Math.floor(number/newBase); //round down the divided answer
} while (number>newBase-1) //loop as long as this condition holds
arr.unshift(number);
return arr;
}
//function to string the arrays
function convertToString(number, base) {
var resultString = ""
for (var i = 0; i < results.length; i++) {
var digit = results[i];
if (digit > 9) {
switch (digit) {
case 10:
digit = 'A'
break;
case 11:
digit = 'B'
break;
case 12:
digit = 'C'
break;
case 13:
digit = 'D'
break;
case 14:
digit = 'E'
break;
case 15:
digit = 'F'
break;
}
}
resultString += digit;
}
return resultString
}
//function to alert the answer statement
function alertAnswer() {
var statement = alert("Base 10 number:" + myNumber + "is" + base + "in" + myBase);
return statement;
}
//function to find the maximum number in the array
function myMax(myArray) {
var max = myArray[0];
for (var z = 0; z < myArray.length; z++) {
if (myArray[z] > max) {
max = myArray[z];
}
}
return max;
}
//function to find the minimum number in the array
function myMin(myArray) {
var min = myArray[0];
for (var z = 0; z < myArray.length; z++) {
if (myArray[z] > min) {
min = myArray[z];
}
}
return min;
}
Sorry if I'm mistaken, but is this what you're looking for?
var promptVar = false;
do
{
//function calls for steps 1~4
prompt();
}
while (promptVar == false)
function prompt()
{
if (confirm('Do you want to continue to step 6?'))
{
promptVar = true;
} else {
promptVar = false;
}
}

Damerau-Levenshtein distance Implementation

I'm trying to create a damerau-levenshtein distance function in JS.
I've found a description off the algorithm on WIkipedia, but they is no implementation off it. It says:
To devise a proper algorithm to calculate unrestricted
Damerau–Levenshtein distance note that there always exists an optimal
sequence of edit operations, where once-transposed letters are never
modified afterwards. Thus, we need to consider only two symmetric ways
of modifying a substring more than once: (1) transpose letters and
insert an arbitrary number of characters between them, or (2) delete a
sequence of characters and transpose letters that become adjacent
after deletion. The straightforward implementation of this idea gives
an algorithm of cubic complexity: O\left (M \cdot N \cdot \max(M, N)
\right ), where M and N are string lengths. Using the ideas of
Lowrance and Wagner,[7] this naive algorithm can be improved to be
O\left (M \cdot N \right) in the worst case. It is interesting that
the bitap algorithm can be modified to process transposition. See the
information retrieval section of[1] for an example of such an
adaptation.
https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
The section [1] points to http://acl.ldc.upenn.edu/P/P00/P00-1037.pdf which is even more complicated to me.
If I understood this correctly, it's not that easy to create an implementation off it.
Here's the levenshtein implementation I currently use :
levenshtein=function (s1, s2) {
// discuss at: http://phpjs.org/functions/levenshtein/
// original by: Carlos R. L. Rodrigues (http://www.jsfromhell.com)
// bugfixed by: Onno Marsman
// revised by: Andrea Giammarchi (http://webreflection.blogspot.com)
// reimplemented by: Brett Zamir (http://brett-zamir.me)
// reimplemented by: Alexander M Beedie
// example 1: levenshtein('Kevin van Zonneveld', 'Kevin van Sommeveld');
// returns 1: 3
if (s1 == s2) {
return 0;
}
var s1_len = s1.length;
var s2_len = s2.length;
if (s1_len === 0) {
return s2_len;
}
if (s2_len === 0) {
return s1_len;
}
// BEGIN STATIC
var split = false;
try {
split = !('0')[0];
} catch (e) {
// Earlier IE may not support access by string index
split = true;
}
// END STATIC
if (split) {
s1 = s1.split('');
s2 = s2.split('');
}
var v0 = new Array(s1_len + 1);
var v1 = new Array(s1_len + 1);
var s1_idx = 0,
s2_idx = 0,
cost = 0;
for (s1_idx = 0; s1_idx < s1_len + 1; s1_idx++) {
v0[s1_idx] = s1_idx;
}
var char_s1 = '',
char_s2 = '';
for (s2_idx = 1; s2_idx <= s2_len; s2_idx++) {
v1[0] = s2_idx;
char_s2 = s2[s2_idx - 1];
for (s1_idx = 0; s1_idx < s1_len; s1_idx++) {
char_s1 = s1[s1_idx];
cost = (char_s1 == char_s2) ? 0 : 1;
var m_min = v0[s1_idx + 1] + 1;
var b = v1[s1_idx] + 1;
var c = v0[s1_idx] + cost;
if (b < m_min) {
m_min = b;
}
if (c < m_min) {
m_min = c;
}
v1[s1_idx + 1] = m_min;
}
var v_tmp = v0;
v0 = v1;
v1 = v_tmp;
}
return v0[s1_len];
}
What are your ideas for building such an algorithm and, if you think it would be too complicated, what could I do to make no difference between 'l' (L lowercase) and 'I' (i uppercase) for example.
The gist #doukremt gave: https://gist.github.com/doukremt/9473228
gives the following in Javascript.
You can change the weights of operations in the weighter object.
var levenshteinWeighted= function(seq1,seq2)
{
var len1=seq1.length;
var len2=seq2.length;
var i, j;
var dist;
var ic, dc, rc;
var last, old, column;
var weighter={
insert:function(c) { return 1.; },
delete:function(c) { return 0.5; },
replace:function(c, d) { return 0.3; }
};
/* don't swap the sequences, or this is gonna be painful */
if (len1 == 0 || len2 == 0) {
dist = 0;
while (len1)
dist += weighter.delete(seq1[--len1]);
while (len2)
dist += weighter.insert(seq2[--len2]);
return dist;
}
column = []; // malloc((len2 + 1) * sizeof(double));
//if (!column) return -1;
column[0] = 0;
for (j = 1; j <= len2; ++j)
column[j] = column[j - 1] + weighter.insert(seq2[j - 1]);
for (i = 1; i <= len1; ++i) {
last = column[0]; /* m[i-1][0] */
column[0] += weighter.delete(seq1[i - 1]); /* m[i][0] */
for (j = 1; j <= len2; ++j) {
old = column[j];
if (seq1[i - 1] == seq2[j - 1]) {
column[j] = last; /* m[i-1][j-1] */
} else {
ic = column[j - 1] + weighter.insert(seq2[j - 1]); /* m[i][j-1] */
dc = column[j] + weighter.delete(seq1[i - 1]); /* m[i-1][j] */
rc = last + weighter.replace(seq1[i - 1], seq2[j - 1]); /* m[i-1][j-1] */
column[j] = ic < dc ? ic : (dc < rc ? dc : rc);
}
last = old;
}
}
dist = column[len2];
return dist;
}
Stolen from here, with formatting and some examples on how to use it:
function DamerauLevenshtein(prices, damerau) {
//'prices' customisation of the edit costs by passing an object with optional 'insert', 'remove', 'substitute', and
//'transpose' keys, corresponding to either a constant number, or a function that returns the cost. The default cost
//for each operation is 1. The price functions take relevant character(s) as arguments, should return numbers, and
//have the following form:
//
//insert: function (inserted) { return NUMBER; }
//
//remove: function (removed) { return NUMBER; }
//
//substitute: function (from, to) { return NUMBER; }
//
//transpose: function (backward, forward) { return NUMBER; }
//
//The damerau flag allows us to turn off transposition and only do plain Levenshtein distance.
if (damerau !== false) {
damerau = true;
}
if (!prices) {
prices = {};
}
let insert, remove, substitute, transpose;
switch (typeof prices.insert) {
case 'function':
insert = prices.insert;
break;
case 'number':
insert = function (c) {
return prices.insert;
};
break;
default:
insert = function (c) {
return 1;
};
break;
}
switch (typeof prices.remove) {
case 'function':
remove = prices.remove;
break;
case 'number':
remove = function (c) {
return prices.remove;
};
break;
default:
remove = function (c) {
return 1;
};
break;
}
switch (typeof prices.substitute) {
case 'function':
substitute = prices.substitute;
break;
case 'number':
substitute = function (from, to) {
return prices.substitute;
};
break;
default:
substitute = function (from, to) {
return 1;
};
break;
}
switch (typeof prices.transpose) {
case 'function':
transpose = prices.transpose;
break;
case 'number':
transpose = function (backward, forward) {
return prices.transpose;
};
break;
default:
transpose = function (backward, forward) {
return 1;
};
break;
}
function distance(down, across) {
//http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
let ds = [];
if (down === across) {
return 0;
} else {
down = down.split('');
down.unshift(null);
across = across.split('');
across.unshift(null);
down.forEach(function (d, i) {
if (!ds[i]) {
ds[i] = [];
}
across.forEach(function (a, j) {
if (i === 0 && j === 0) {
ds[i][j] = 0;
} else if (i === 0) {
//Empty down (i == 0) -> across[1..j] by inserting
ds[i][j] = ds[i][j - 1] + insert(a);
} else if (j === 0) {
//Down -> empty across (j == 0) by deleting
ds[i][j] = ds[i - 1][j] + remove(d);
} else {
//Find the least costly operation that turns the prefix down[1..i] into the prefix across[1..j] using
//already calculated costs for getting to shorter matches.
ds[i][j] = Math.min(
//Cost of editing down[1..i-1] to across[1..j] plus cost of deleting
//down[i] to get to down[1..i-1].
ds[i - 1][j] + remove(d),
//Cost of editing down[1..i] to across[1..j-1] plus cost of inserting across[j] to get to across[1..j].
ds[i][j - 1] + insert(a),
//Cost of editing down[1..i-1] to across[1..j-1] plus cost of substituting down[i] (d) with across[j]
//(a) to get to across[1..j].
ds[i - 1][j - 1] + (d === a ? 0 : substitute(d, a))
);
//Can we match the last two letters of down with across by transposing them? Cost of getting from
//down[i-2] to across[j-2] plus cost of moving down[i-1] forward and down[i] backward to match
//across[j-1..j].
if (damerau && i > 1 && j > 1 && down[i - 1] === a && d === across[j - 1]) {
ds[i][j] = Math.min(ds[i][j], ds[i - 2][j - 2] + (d === a ? 0 : transpose(d, down[i - 1])));
}
}
});
});
return ds[down.length - 1][across.length - 1];
}
}
return distance;
}
//Returns a distance function to call.
let dl = DamerauLevenshtein();
console.log(dl('12345', '23451'));
console.log(dl('this is a test', 'this is not a test'));
console.log(dl('testing testing 123', 'test'));

Convert LaTeX to dynamic Javascript function

I have a user input for an equation - this input generates LaTeX code using a separate API which I did not code (namely, Mathquill, not that it matters).
My problem is best illustrated by an example: suppose the LaTeX code generated from the user input was this:
x^2+3x-10sin\left(2x\right)
How would I convert this (on the fly of course) into a JavaScript function which, hard-coded, would look like this:
function(x) {
return Math.pow(x, 2) + 3 * x - 10 * Math.sin(2 * x);
}
Are there any APIs or am I looking at writing something which will interpret the LaTeX symbols and make a function, somehow? Or what?
I have written a (by no means general purpose) solution, heavily based on George's code.
Here it is:
var CALC_CONST = {
// define your constants
e: Math.E,
pi: Math.PI
};
var CALC_NUMARGS = [
[/^(\^|\*|\/|\+|\-)$/, 2],
[/^(floor|ceil|(sin|cos|tan|sec|csc|cot)h?)$/, 1]
];
var Calc = function(expr, infix) {
this.valid = true;
this.expr = expr;
if (!infix) {
// by default treat expr as raw latex
this.expr = this.latexToInfix(expr);
}
var OpPrecedence = function(op) {
if (typeof op == "undefined") return 0;
return op.match(/^(floor|ceil|(sin|cos|tan|sec|csc|cot)h?)$/) ? 10
: (op === "^") ? 9
: (op === "*" || op === "/") ? 8
: (op === "+" || op === "-") ? 7
: 0;
}
var OpAssociativity = function(op) {
return op.match(/^(floor|ceil|(sin|cos|tan|sec|csc|cot)h?)$/) ? "R" : "L";
}
var numArgs = function(op) {
for (var i = 0; i < CALC_NUMARGS.length; i++) {
if (CALC_NUMARGS[i][0].test(op)) return CALC_NUMARGS[i][1];
}
return false;
}
this.rpn_expr = [];
var rpn_expr = this.rpn_expr;
this.expr = this.expr.replace(/\s+/g, "");
// This nice long regex matches any valid token in a user
// supplied expression (e.g. an operator, a constant or
// a variable)
var in_tokens = this.expr.match(/(\^|\*|\/|\+|\-|\(|\)|[a-zA-Z0-9\.]+)/gi);
var op_stack = [];
in_tokens.forEach(function(token) {
if (/^[a-zA-Z]$/.test(token)) {
if (CALC_CONST.hasOwnProperty(token)) {
// Constant. Pushes a value onto the stack.
rpn_expr.push(["num", CALC_CONST[token]]);
}
else {
// Variables (i.e. x as in f(x))
rpn_expr.push(["var", token]);
}
}
else {
var numVal = parseFloat(token);
if (!isNaN(numVal)) {
// Number - push onto the stack
rpn_expr.push(["num", numVal]);
}
else if (token === ")") {
// Pop tokens off the op_stack onto the rpn_expr until we reach the matching (
while (op_stack[op_stack.length - 1] !== "(") {
rpn_expr.push([numArgs(op_stack[op_stack.length - 1]), op_stack.pop()]);
if (op_stack.length === 0) {
this.valid = false;
return;
}
}
// remove the (
op_stack.pop();
}
else if (token === "(") {
op_stack.push(token);
}
else {
// Operator
var tokPrec = OpPrecedence(token),
headPrec = OpPrecedence(op_stack[op_stack.length - 1]);
while ((OpAssociativity(token) === "L" && tokPrec <= headPrec) ||
(OpAssociativity(token) === "R" && tokPrec < headPrec)) {
rpn_expr.push([numArgs(op_stack[op_stack.length - 1]), op_stack.pop()]);
if (op_stack.length === 0) break;
headPrec = OpPrecedence(op_stack[op_stack.length - 1]);
}
op_stack.push(token);
}
}
});
// Push all remaining operators onto the final expression
while (op_stack.length > 0) {
var popped = op_stack.pop();
if (popped === ")") {
this.valid = false;
break;
}
rpn_expr.push([numArgs(popped), popped]);
}
}
/**
* returns the result of evaluating the current expression
*/
Calc.prototype.eval = function(x) {
var stack = [], rpn_expr = this.rpn_expr;
rpn_expr.forEach(function(token) {
if (typeof token[0] == "string") {
switch (token[0]) {
case "var":
// Variable, i.e. x as in f(x); push value onto stack
//if (token[1] != "x") return false;
stack.push(x);
break;
case "num":
// Number; push value onto stack
stack.push(token[1]);
break;
}
}
else {
// Operator
var numArgs = token[0];
var args = [];
do {
args.unshift(stack.pop());
} while (args.length < numArgs);
switch (token[1]) {
/* BASIC ARITHMETIC OPERATORS */
case "*":
stack.push(args[0] * args[1]);
break;
case "/":
stack.push(args[0] / args[1]);
break;
case "+":
stack.push(args[0] + args[1]);
break;
case "-":
stack.push(args[0] - args[1]);
break;
// exponents
case "^":
stack.push(Math.pow(args[0], args[1]));
break;
/* TRIG FUNCTIONS */
case "sin":
stack.push(Math.sin(args[0]));
break;
case "cos":
stack.push(Math.cos(args[0]));
break;
case "tan":
stack.push(Math.tan(args[0]));
break;
case "sec":
stack.push(1 / Math.cos(args[0]));
break;
case "csc":
stack.push(1 / Math.sin(args[0]));
break;
case "cot":
stack.push(1 / Math.tan(args[0]));
break;
case "sinh":
stack.push(.5 * (Math.pow(Math.E, args[0]) - Math.pow(Math.E, -args[0])));
break;
case "cosh":
stack.push(.5 * (Math.pow(Math.E, args[0]) + Math.pow(Math.E, -args[0])));
break;
case "tanh":
stack.push((Math.pow(Math.E, 2*args[0]) - 1) / (Math.pow(Math.E, 2*args[0]) + 1));
break;
case "sech":
stack.push(2 / (Math.pow(Math.E, args[0]) + Math.pow(Math.E, -args[0])));
break;
case "csch":
stack.push(2 / (Math.pow(Math.E, args[0]) - Math.pow(Math.E, -args[0])));
break;
case "coth":
stack.push((Math.pow(Math.E, 2*args[0]) + 1) / (Math.pow(Math.E, 2*args[0]) - 1));
break;
case "floor":
stack.push(Math.floor(args[0]));
break;
case "ceil":
stack.push(Math.ceil(args[0]));
break;
default:
// unknown operator; error out
return false;
}
}
});
return stack.pop();
};
Calc.prototype.latexToInfix = function(latex) {
/**
* function: converts latex notation to infix notation (human-readable, to be converted
* again to prefix in order to be processed
*
* Supported functions / operators / notation:
* parentheses, exponents, adding, subtracting, multipling, dividing, fractions
* trigonometric (including hyperbolic) functions, floor, ceil
*/
var infix = latex;
infix = infix
.replace(/\\frac{([^}]+)}{([^}]+)}/g, "($1)/($2)") // fractions
.replace(/\\left\(/g, "(") // open parenthesis
.replace(/\\right\)/g, ")") // close parenthesis
.replace(/[^\(](floor|ceil|(sin|cos|tan|sec|csc|cot)h?)\(([^\(\)]+)\)[^\)]/g, "($&)") // functions
.replace(/([^(floor|ceil|(sin|cos|tan|sec|csc|cot)h?|\+|\-|\*|\/)])\(/g, "$1*(")
.replace(/\)([\w])/g, ")*$1")
.replace(/([0-9])([A-Za-z])/g, "$1*$2")
;
return infix;
};
Example of usage:
var latex = "e^x+\\frac{2}{3}x-4sin\\left(x\\right)";
var calc = new Calc(latex);
var test = calc.eval(3.5); // 36.85191820278412
Well, you're going to have to decide on exactly which operations you support at some point. After that it shouldn't be hard to implement an evaluator using a parser like the Shunting-yard algorithm to yield a representation of the equation that is more easy to evaluate (that is, an abstract syntax tree).
I have a simple example of this kind of evaluator written in JavaScript at: http://gjp.cc/projects/logic_tables.html It takes logical expressions like !(p ^^ q) & ~(p || q) instead of LaTeX, but it might still be a useful example for you.
The JavaScript (http://gpittarelli.com/projects/logic_tables.js):
var CALCULATOR_CONSTANTS = {
/* True values. */
't': true,
'true': true,
/* False values. */
'c': false,
'false': false
};
// The Calculator constructor takes an expression and parses
// it into an AST (refered to as rpn_expr)
var Calculator = function(expr) {
this.valid = true;
var OpPrecedence = function(op) {
return (op === "!" || op === "~")? 9
: (op === "&" || op === "&&")? 7
: (op === "|" || op === "||" )? 7
: (op === "^" || op === "^^")? 7
: (op === "->")? 5
: (op === "<-")? 5
: 0;
}
var OpAssociativity = function(op) {
return (op === "!" || op === "~")? "R":"L";
}
this.rpn_expr = [];
this.variables = [];
var rpn_expr = this.rpn_expr;
var variables = this.variables;
expr = expr.replace(/\s+/g, "");
// This nice long regex matches any valid token in a user
// supplied expression (e.g. an operator, a constant or
// a variable)
var in_tokens = expr.match(/(\!|\~|\|+|&+|\(|\)|\^+|(->)|(<-)|[a-zA-Z0-9]+)/gi);
var op_stack = [];
in_tokens.forEach(function(token) {
if (/[a-zA-Z0-9]+/.test(token)) {
if (CALCULATOR_CONSTANTS.hasOwnProperty(token)) {
// Constant. Pushes a boolean value onto the stack.
rpn_expr.push(CALCULATOR_CONSTANTS[token]);
} else {
// Variables
rpn_expr.push(token);
variables.push(token);
}
}
else if (token === ")") {
// Pop tokens off the op_stack onto the rpn_expr until we
// reach the matching (
while (op_stack[op_stack.length-1] !== "(") {
rpn_expr.push(op_stack.pop());
if (op_stack.length === 0) {
this.valid = false;
return;
}
}
// Remove the (
op_stack.pop();
}
else if (token === "(") {
op_stack.push(token);
}
else {
// Operator
var tokPrec = OpPrecedence( token ),
headPrec = OpPrecedence( op_stack[op_stack.length-1] );
while ((OpAssociativity(token) === "L" && tokPrec <= headPrec)
|| (OpAssociativity(token) === "R" && tokPrec < headPrec) ) {
rpn_expr.push(op_stack.pop());
if (op_stack.length === 0)
break;
headPrec = OpPrecedence( op_stack[op_stack.length-1] );
}
op_stack.push(token);
}
});
// Push all remaining operators onto the final expression
while (op_stack.length > 0) {
var popped = op_stack.pop();
if (popped === ")") {
this.valid = false;
break;
}
rpn_expr.push(popped);
}
this.optimize();
}
/** Returns the variables used in the currently loaded expression. */
Calculator.prototype.getVariables = function() { return this.variables; }
Calculator.prototype.optimize = function() {
// Single-pass optimization, mainly just to show the concept.
// Looks for statements that can be pre computed, eg:
// p | true
// q & false
// r ^ r
// etc...
// We do this by reading through the RPN expression as if we were
// evaluating it, except instead rebuild it as we go.
var stack = [], rpn_expr = this.rpn_expr;
rpn_expr.forEach(function(token) {
if (typeof token === "boolean") {
// Constant.
stack.push(token);
} else if (/[a-zA-Z0-9]+/.test(token)) {
// Identifier - push onto the stack
stack.push(token);
} else {
// Operator - The actual optimization takes place here.
// TODO: Add optimizations for more operators.
if (token === "^" || token === "^^") {
var a = stack.pop(), b = stack.pop();
if (a === b) { // p ^ p == false
stack.push(false);
} else {
stack.push(b);
stack.push(a);
stack.push(token);
}
} else if (token === "|" || token === "||") {
var a = stack.pop(), b = stack.pop();
if (a === true || b === true) {
// If either of the operands is a tautology, OR is
// also a tautology.
stack.push(true);
} else if (a === b) { // p | p == p
stack.push(a);
} else {
stack.push(b);
stack.push(a);
stack.push(token);
}
} else if (token === "!" || token === "~") {
var p = stack.pop();
if (typeof p === "boolean") {
// NOT of a constant value can always
// be precalculated.
stack.push(!p);
} else {
stack.push(p);
stack.push(token);
}
} else {
stack.push(token);
}
}
});
this.rpn_expr = stack;
}
/**
* returns the result of evaluating the current expressions
* with the passed in <code>variables</code> object. <i>variables</i>
* should be an object who properties map from key => value
*/
Calculator.prototype.eval = function(variables) {
var stack = [], rpn_expr = this.rpn_expr;
rpn_expr.forEach(function(token) {
if (typeof token === "boolean") {
// Constant.
stack.push(token);
} else if (/[a-zA-Z0-9]+/.test(token)) {
// Identifier - push its boolean value onto the stack
stack.push(!!variables[token]);
} else {
// Operator
var q = stack.pop(), p = stack.pop();
if (token === "^" || token === "^^") {
stack.push((p? 1:0) ^ (q? 1:0));
} else if (token === "|" || token === "||") {
stack.push(p || q);
} else if (token === "&" || token === "&&") {
stack.push(p && q);
} else if (token === "!" || token === "~") {
stack.push(p);
stack.push(!q);
} else if (token === "->") {
stack.push((!p) || q);
} else if (token === "<-") {
stack.push((!q) || p);
}
}
});
return stack.pop()? 1:0;
};
Maybe you could try LatexJS. LatexJS is an API service that I put together in order to convert latex math notation into Javascript functions. So you would input latex expressions and get back Javascript functions dynamically. For example:
Input
x^2+3x-10sin\left(2x\right)
Output
{
"func": "(x)=>{return Math.pow(x,2)+3*x-10*Math.sin(2*x)};",
"params": ["x"]
}
Evaluation
> func = (x)=>{return Math.pow(x,2)+3*x-10*Math.sin(2*x)};
> func(2)
< 17.56802495307928

Categories