Expansion of algebraic term - javascript

I am trying to expand an algebraic term.
(x+1)(x+1)/x => x + 2 + x^-1
(x+1)^3 => x^3 + 3x^2 + 3x + 1
(x^2*x)(x^2) => x^5
This is my attempt at it. I have tried a lot of ways trying to fix the problems below.
Problems:
Like terms should be added together
(x+1)(x+1)(x+1) should be valid.
(x+1)^2 should be equal to (x+1)(x+1)
x(x+1) should be valid
1x^n should just be x^n
There should be no 0x^n terms.
nx^0 terms should just be n
Code Snippet:
function split(input) {
return ((((input.split(")(")).toString()).replace(/\)/g, "")).replace(/\(/g, "")).split(','); }
function strVali(str) {
str = str.replace(/\s+/g, "");
var parts = str.match(/[+\-]?[^+\-]+/g);
// accumulate the results
return parts.reduce(function(res, part) {
var coef = parseFloat(part) || +(part[0] + "1") || 1;
var x = part.indexOf('x');
var power = x === -1 ?
0:
part[x + 1] === "^" ?
+part.slice(x + 2) :
1;
res[power] = (res[power] || 0) + coef;
return res;
}, {});
}
function getCoeff(coeff) {
var powers = Object.keys(strVali(coeff));
var max = Math.max.apply(null, powers);
var result = [];
for(var i = max; i >= 0; i--)
result.push(strVali(coeff)[i] || 0);
return result; }
function evaluate(expression) {
var term1 = getCoeff(expression[0]);
var term2 = getCoeff(expression[1]);
var expand = "";
for ( var j = 0; j < term1.length; j++ ) {
for ( var i = 0; i < term2.length; i++ ) {
expand += Number(term1[j] * term2[i]) + 'x^ ' + (Number(term1.length) - 1 - j + Number(term2.length) - 1 - i) + ' + ';
}}
var final = "";
for ( var Z = 0; Z < getCoeff(expand).length; Z++) {
final += ' ' + getCoeff(expand)[Z] + 'x^ {' + (getCoeff(expand).length - Z - 1) + '} +';
}
final = "$$" + ((((((final.replace(/\+[^\d]0x\^ \{[\d]+\}/g,'')).replace(/x\^ \{0}/g,'')).replace(/x\^ \{1}/g,'x')).replace(/[^\d]1x\^ /g,'+ x^')).replace(/\+ -/g,' - ')).slice(0, -1)).substring(1,(final.length)) + "$$";
document.getElementById('result').innerHTML = final;
MathJax.Hub.Queue(["Typeset", MathJax.Hub, document.getElementById('result')]);
}
function caller() {
var input = document.getElementById('input').value;
evaluate(split(input)); }
div.wrapper {
width: 100%;
height:100%;
border:0px solid black;
}
input[type="text"] {
display: block;
margin : 0 auto;
padding: 10px;
font-size:20px;
}
button{
margin:auto;
display:block;
background-color: white;
color: black;
border: 2px solid #555555;
padding-left: 20px;
padding-right: 20px;
font-size: 20px;
margin-top:10px;
}
button:hover {
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
}
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
<div class='wrapper'><input id="input" title="Enter Expression" type="text" value="(x^2+x+1)(x^2+x+1)"></div>
<div> <button onclick="caller()">Click</button></div>
<div id="result">$$x^4 + 2x^3 + 3x^2 + 2x + 1$$</div>
Reference:
How to calculate coefficients of polynomial expansion in javascript
Getting coefficients of algebraic term
How to get a term before a character?

Edit: I have re-written my original answer from when the question did not include the - and / operators). My new answer supports - and /.
Since you have not defined a precise grammar, I have made some assumptions. I support the +, -, /, ^ operators and the implicit multiply. These can operate on numbers, x and (...) expressions, except that right hand side of the ^ operator must be a number. I allow spaces between tokens.
Limitations: The -is a binary not unary - operator, So x-2 if OK, -2 on its own is a syntax error. The / is limited. You may divide by a value with a single coefficient such as 2, 'x', 2x^2, but not by cannot have 1/(x^2+1), which would evaluate to an infinite series.
It first takes the string and wraps it in a 'tokenizer' which lets you look it one token at a time, where a token is a number, x, ( ) or operator.
It then calls evaluateSum() which evaluates things separated by + or -, where each thing is a product evaluated by evaluateProduct(). This in turn uses evaluatePower() to evalue the ^. Finally evaluateTerm() looks for x, numbers and bracketed sub-expressions which are evaluates recursively with evaluateSum(). This hierarchy creates the correct operator precedence and evaluation order.
Each layer of the evaluation returns a 'coefficients' object which s array-like, but may contain megative indexes. For example [1,0,1] means 1+x^2.
It can cope with any number of nested brackets and a lot of other case such as xx(x) is x^3, 2^3 is 8. I have added a few throws for syntax errors, for example 2^x is illegal.
function makeTokenizer(source) {
var c, i, tokenizer;
i = 0; // The index of the current character.
c = source.charAt(i); // The current character.
function consumeChar() { // Move to next c, return previous c.
var prevC = c;
i += 1;
c = source.charAt(i);
return prevC;
}
tokenizer = {
next : function () {
var str;
while (c === ' ') { // Skip spaces
consumeChar();
}
if (!c) {
tokenizer.token = undefined; // End of source
} else if (c >= '0' && c <= '9') { // number
str = consumeChar(); // First digit
while (c >= '0' && c <= '9') { // Look for more digits.
str += consumeChar();
}
tokenizer.token = Number(str);
} else if (c === "x") {
tokenizer.token = consumeChar();
} else { // single-character operator
tokenizer.token = consumeChar();
}
}
};
tokenizer.next(); // Load first token.
return tokenizer;
}
function makeCoefficients() { // Make like an array but can have -ve indexes
var min = 0, max = 0, c = {};
return {
get: function (i) {
return c[i] || 0;
},
set: function (i, value) {
c[i] = value;
min = Math.min(i, min);
max = Math.max(i + 1, max);
return this; // for chaining
},
forEach: function (callback) {
var i;
for (i = min; i < max; i += 1) {
if (this.get(i)) {
callback(this.get(i), i);
}
}
},
toString: function () {
var result = "", first = true;
this.forEach(function (val, power) {
result += (val < 0 || first) ? "" : "+";
first = false;
result += (val === 1 && power !== 0) ? "" : val;
if (power) {
result += "x";
if (power !== 1) {
result += "^" + power;
}
}
});
return result;
},
toPowerOf: function (power) {
if (power === 0) {
return makeCoefficients().set(0, 1); // Anything ^0 = 1
}
if (power === 1) {
return this;
}
if (power < 0) {
throw "cannot raise to negative powers";
}
return this.multiply(this.toPowerOf(power - 1));
},
multiply: function (coefficients) {
var result = makeCoefficients();
this.forEach(function (a, i) {
coefficients.forEach(function (b, j) {
result.set(i + j, result.get(i + j) + a * b);
});
});
return result;
},
divide: function (coefficients) {
// Division is hard, for example we cannot do infinite series like:
// 1/(1 + x^2) = sum_(n=0 to infinity) 1/2 x^n ((-i)^n + i^n)
// So we do a few easy cases only.
var that = this, result = makeCoefficients(), done;
coefficients.forEach(function (value, pow) {
that.forEach(function (value2, pow2) {
result.set(pow2 - pow, value2 / value);
});
if (done) {
throw "cannot divide by " + coefficients.toString();
}
done = true;
});
return result;
},
add: function (coefficients, op) {
var result = makeCoefficients();
this.forEach(function (value, i) {
result.set(value, i);
});
op = (op === "-" ? -1 : 1);
coefficients.forEach(function (value, i) {
result.set(i, result.get(i) + value * op);
});
return result;
}
};
}
var evaluateSum; // Called recursively
function evaluateTerm(tokenizer) {
var result;
if (tokenizer.token === "(") {
tokenizer.next();
result = evaluateSum(tokenizer);
if (tokenizer.token !== ")") {
throw ") missing";
}
tokenizer.next();
} else if (typeof tokenizer.token === "number") {
result = makeCoefficients().set(0, tokenizer.token);
tokenizer.next();
} else if (tokenizer.token === "x") {
tokenizer.next();
result = makeCoefficients().set(1, 1); // x^1
} else {
return false; // Not a 'term'
}
return result;
}
function evaluatePower(tokenizer) {
var result;
result = evaluateTerm(tokenizer);
if (tokenizer.token === "^") {
tokenizer.next();
if (typeof tokenizer.token !== "number") {
throw "number expected after ^";
}
result = result.toPowerOf(tokenizer.token);
tokenizer.next();
}
return result;
}
function evaluateProduct(tokenizer) {
var result, term, divOp;
result = evaluatePower(tokenizer);
if (!result) {
throw "Term not found";
}
while (true) {
divOp = (tokenizer.token === "/");
if (divOp) {
tokenizer.next();
term = evaluatePower(tokenizer);
result = result.divide(term);
} else {
term = evaluatePower(tokenizer);
if (!term) {
break;
}
result = result.multiply(term);
}
}
return result;
}
function evaluateSum(tokenizer) {
var result, op;
result = evaluateProduct(tokenizer);
while (tokenizer.token === "+" || tokenizer.token === "-") {
op = tokenizer.token;
tokenizer.next();
result = result.add(evaluateProduct(tokenizer), op);
}
return result;
}
function evaluate(source) {
var tokenizer = makeTokenizer(source),
coefficients = evaluateSum(tokenizer);
if (tokenizer.token) {
throw "Unexpected token " + tokenizer.token;
}
console.log(source + " => " + coefficients.toString());
}
// Examples:
evaluate("(x+1)(x+1)"); // => 1+2x+x^2
evaluate("(x+1)^2"); // => 1+2x+x^2
evaluate("(x+1)(x+1)(x+1)"); // => 1+3x+3x^2+x^3
evaluate("(x+1)^3"); // => 1+3x+3x^2+x^3
evaluate("(x)(x+1)"); // => x+x^2
evaluate("(x+1)(x)"); // => x+x^2
evaluate("3x^0"); // => 3
evaluate("2^3"); // => 8
evaluate("(x+2x(x+2(x+1)x))"); // => x+6x^2+4x^3
evaluate("(x+1)(x-1)"); // => -1+x^2
evaluate("(x+1)(x+1)/x"); // x^-1+2+x
//evaluate("(x+1)/(x^2 + 1)"); //throws cannot divide by 1+2x

It can handle +, -, / and implicit multiplication. It expands the parenthesis accordingly and adds them to the original expression while removing the parenthesised version. It collects like terms accordingly. Limitations: It cannot divide by a polynomial and it does not support the * operator.
function strVali(str) {
str = str.replace(/\s+/g, "");
var parts = str.match(/[+\-]?[^+\-]+/g);
return parts.reduce(function(res, part) {
var coef = parseFloat(part) || +(part[0] + "1") || 1;
var x = part.indexOf('x');
var power = x === -1 ?
0:
part[x + 1] === "^" ?
+part.slice(x + 2) :
1;
res[power] = (res[power] || 0) + coef;
return res;
}, {});
}
function getCoeff(coeff) {
var powers = Object.keys(strVali(coeff));
var max = Math.max.apply(null, powers);
var result = [];
for(var i = max; i >= 0; i--)
result.push(strVali(coeff)[i] || 0);
return result; }
function format(str) {
str = ' ' + str;
str = str.replace(/-/g,'+-').replace(/x(?!\^)/g,'x^1').replace(/([+\/*)(])(\d+)([+\/*)(])/g,'$1$2x^0$3').replace(/([^\d])(x\^-?\d+)/g,'$11$2').replace(/(-?\d+x\^\d+)(?=\()/g,'($1)').replace(/(\))(-?\d+x\^\d+)/g,'$1($2)').replace(/([^\)\/])(\()([^\*\/\(\)]+?)(\))(?![(^\/])/g,'$1$3');
str = str.replace(/(\([^\(\)]+?\))\/(\d+x\^-?\d+)/g,'$1/($2)').replace(/(\d+x\^-?\d+)\/(\d+x\^-?\d+)/g,'($1)/($2)').replace(/(\d+x\^-?\d+)\/(\(\d+x\^-?\d+\))/g,'($1)/$2');
return str;
}
function expBrackets(str) {
var repeats = str.match(/\([^\(\)]+?\)\^\d+/g);
if ( repeats === null ) { return str; } else { var totalRepeat = '';
for ( var t = 0; t < repeats.length; t++ ) { var repeat = repeats[t].match(/\d+$/); for ( var u = 0; u < Number(repeat); u++ ) { totalRepeat += repeats[t].replace(/\^\d+$/,''); }
str = str.replace(/\([^\(\)]+?\)\^\d+/, totalRepeat); totalRepeat = ''; }
return str; }
}
function multiply(str) {
var pairs = str.match(/\([^\(\)]+?\)\([^\(\)]+?\)/g);
if ( pairs !== null ) { while ( pairs !== null ) { var output = '';
for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].slice(1).slice(0, -1).split(')('); var firstCoeff = getCoeff(pair[0]); var secondCoeff = getCoeff(pair[1]);
for (var j = 0; j < firstCoeff.length; j++) {
for (var k = 0; k < secondCoeff.length; k++) { output += firstCoeff[j] * secondCoeff[k] + 'x^' + Number(firstCoeff.length - 1 - j + secondCoeff.length - 1 - k) + '+'; } }
var regexp = new RegExp(pairs[i].replace(/\(/g,'\\(').replace(/\+/g,'\\+').replace(/\)/g,'\\)').replace(/\^/g,'\\^').replace(/\-/g,'\\-'));
str = str.replace(regexp, '(' + (output.slice(0, -1).replace(/[^\d]0x\^\d+/g,'')) + ')');
output = ''; }
pairs = str.match(/\([^\(\)]+?\)\([^\(\)]+?\)/g); } }
else { }
str = str.replace(/\+/g,' + ');
return str;
}
function divide(str) {
if ( str.match(/\/(\(-?\d+x\^-?\d+.+?\))/g) === null && str.match(/\//g) !== null ) {
while ( pairs !== null ) {
var pairs = str.match(/\([^\(\)]+?\)\/\([^\(\)]+?\)/g);
var output = '';
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].slice(1).slice(0, -1).split(')/(');
var firstCoeff = getCoeff(pair[0]);
var secondCoeff = getCoeff(pair[1]);
for (var j = 0; j < firstCoeff.length; j++) {
for (var k = 0; k < secondCoeff.length; k++) {
output += firstCoeff[j] / secondCoeff[k] + 'x^' + Number(firstCoeff.length - 1 - j - secondCoeff.length + 1 + k) + '+';
output = output.replace(/([+-])Infinityx\^\-?\d+/g,'').replace(/([+-])NaNx\^\-?\d+/g,'');
} }
var regexp = new RegExp(pairs[i].replace(/\(/g,'\\(').replace(/\+/g,'\\+').replace(/\)/g,'\\)').replace(/\^/g,'\\^').replace(/\-/g,'\\-'));
str = str.replace(regexp, '(' + (output.slice(0, -1).replace(/[^\d]0x\^-?\d+/g,'')) + ')');
output = ''; }
pairs = str.match(/\([^\(\)]+?\)\/\([^\(\)]+?\)/g); } }
else { }
return str;
}
function evaluate(str) {
var result = format(divide(format(multiply(expBrackets(format((str)))))));
var resultCollect = '';
result = result.replace(/\s+/g, "").replace(/[^\d]0x\^-?\d+/g,'').replace(/\+/g,' + ');
if ( result === '') {
document.getElementById('result').innerHTML = '$$' + str + '$$' + '$$ = 0 $$';
MathJax.Hub.Queue(["Typeset", MathJax.Hub, document.getElementById('result')]);
} else if ( result.match(/-?\d+x\^-\d+/g) === null && str.match(/\/(\(-?\d+x\^-?\d+.+?\))/g) === null) {
for ( var i = 0; i < getCoeff(result).length; i++ ) {
resultCollect += getCoeff(result)[i] + 'x^' + Number(getCoeff(result).length - 1 - i) + '+' ; }
if ( resultCollect !== '')
resultCollect = '$$ = ' + resultCollect.slice(0,-1).replace(/[^\d]0x\^-?\d+/g,'').replace(/\+/g,' + ').replace(/x\^0/g,'').replace(/x\^1(?!\d+)/g,'x').replace(/\^(-?\d+)/g,'\^\{$1\}').replace(/\+ -/g,' - ') + '$$';
else
resultCollect = 'Error: Trying to divide by a polynomial ';
document.getElementById('result').innerHTML = '$$' + str.replace(/\^(-?\d+)/g,'\^\{$1\}') + '$$' + resultCollect;
MathJax.Hub.Queue(["Typeset", MathJax.Hub, document.getElementById('result')]);
} else {
resultCollect = '$$ = ' + result.replace(/\^(-?\d+)/g,'\^\{$1\}') + '$$';
document.getElementById('result').innerHTML = '$$' + str.replace(/\^(-?\d+)/g,'\^\{$1\}').replace(/\+ -/g,' - ') + '$$' + resultCollect;
MathJax.Hub.Queue(["Typeset", MathJax.Hub, document.getElementById('result')]);
}
}
function caller() {
var input = document.getElementById('input').value;
evaluate(input);
}
div.wrapper {
width: 100%;
height:100%;
border:0 solid black;
}
input[type="text"] {
display: block;
margin : 0 auto;
padding: 10px;
font-size:20px;
}
button{
margin:auto;
display:block;
background-color: white;
color: black;
border: 2px solid #555555;
padding-left: 20px;
padding-right: 20px;
font-size: 20px;
margin-top:10px;
}
button:hover {
box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24),0 17px 50px 0 rgba(0,0,0,0.19);
}
<script type="text/javascript" async
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML">
</script>
<input id="input" type="text" title="Enter Expression: ">
<button onclick="caller()">Click</button>
<div id="result"></div>
<div id="errors"></div>

My approach utilizes two helper classes:
-Term: This stores a coefficient and an object, the keys of which are variables and the values of which are the exponents. It has methods for determining whether terms are the "same" (i.e. whether they have the same variables with the same exponents, thus allowing them to be added), adding terms, and multiplying terms.
-Polynomial: This stores an array of terms. It has methods for adding two polynomials, multiplying two polynomials, and simplifying polynomials (eliminating terms with 0 coefficients and combining like terms).
It has two substantial helper functions:
-findOuterParens - Given a string, this function returns the indices of the first outermost pair of left and right parentheses.
-parseExpressionStr - This function uses a regular expression to split a string into three types of substrings: 1) a number, 2) a symbol (i.e. a variable, like 'x' or 'y'), or 3) an operator (*, -, +, ^, /). It creates Polynomial objects for the first two types and leaves the operators as strings.
The expand function runs the show. It takes in a string and returns a Polynomial object representing the expanded form of the polynomial in the string. It does this as follows:
1) It deals with parentheses by recursion. It uses findOuterParens to find all outermost parenthsized substrings. So for "3x*(x+4)+(2(y+1))*t", for instance, it would find "x+4" and "2(y+1)" as parenthsized substrings. It then passes these to itself for further parsing. For "2(y+1)", it would identify "y+1" as a parenthesized substring, and pass this to itself, for three levels of recursion for this example.
2) It deals with other parts of the string using parseExpressionStr. After steps 1 and 2, it has an array containing Polynomial objects and operators (stored as strings). It then proceeds to simplifying and expanding.
3) It converts subtraction subproblems to addition subproblems by replacing all -'s with +'s and multiplying all polynomials following -'s by -1.
4) It converts division subproblems to multiplication subproblems by replacing /'s with *'s and inverting the Polynomial following the /. If the polynomial following the / has more than one term, it throws an error.
5) It converts power subproblems to multiplication subproblems by replacing ^'s with a series of multiplications.
6) It adds in implicit *'s. I.e. if two Polynomial elements are right next to each other in the array, then it's inferred that they are being multiplied, so, e.g. '2*x' == '2x'.
7) Now the array has polynomial objects alternating with either '+' or '*'. It first performs all polynomial multiplications and then performs all additions.
8) It performs a final simplification step and returns the resulting expanded polynomial.
<html>
<head></head>
<body></body>
<script>
function findOuterParens(str) {
var leftIndex = str.indexOf('('), leftNum = 1, rightNum = 0;
if (leftIndex === -1)
return
for (var i=leftIndex+1; i<str.length; i++) {
if (str[i] === '(')
leftNum++;
else if (str[i] === ')')
rightNum++, rightIndex=i;
if (leftNum === rightNum)
return {start: leftIndex, end: rightIndex}
}
throw Error('Parenthesis at position ' + leftIndex + ' of "' + str + '" is unpaired.');
}
function parseExpressionStr(inputString) {
var result = [], str = inputString;
while (str) {
var nextPart = str.match(/([\d]+)|([\+\-\^\*\/])|([a-zA-z])/);
if (!nextPart)
return result;
if (nextPart.length === 0)
throw Error('Unable to parse expression string "' + inputString + '". Remainder "' + str + '" could not be parsed.');
else if (nextPart[1]) // First group (digits) matched
result.push(new Polynomial(parseFloat(nextPart[0])));
else if (nextPart[3]) // Third group (symbol) matched
result.push(new Polynomial(nextPart[0]));
else // Second group (operator) matched
result.push(nextPart[0]);
str = str.substring(nextPart.index+nextPart[0].length);
}
return result
}
function isOperator(char) {
return char === '*' || char === '/' || char === '^' || char === '+' || char === '-';
}
function Polynomial(value) {
this.terms = (value!==undefined) ? [new Term(value)] : [];
}
Polynomial.prototype.simplify = function() {
for (var i=0; i<this.terms.length-1; i++) {
if (this.terms[i].coeff === 0) {
this.terms.splice(i--, 1);
continue;
}
for (var j=i+1; j<this.terms.length; j++) {
if (Term.same(this.terms[i], this.terms[j])) {
this.terms[i] = Term.add(this.terms[i], this.terms[j]);
this.terms.splice(j--, 1);
}
}
}
}
Polynomial.add = function(a, b) {
var result = new Polynomial();
result.terms = a.terms.concat(b.terms);
result.simplify();
return result
}
Polynomial.multiply = function(a, b) {
var result = new Polynomial();
a.terms.forEach(function(aTerm) {
b.terms.forEach(function (bTerm) {
result.terms.push(Term.multiply(aTerm, bTerm));
});
});
result.simplify();
return result
}
Polynomial.prototype.toHtml = function() {
var html = ''
for (var i=0; i<this.terms.length; i++) {
var term = this.terms[i];
if (i !== 0)
html += (term.coeff < 0) ? ' - ' : ' + ';
else
html += (term.coeff < 0) ? '-' : '';
var coeff = Math.abs(term.coeff);
html += (coeff === 1 && Object.keys(term.symbols).length > 0) ? '' : coeff;
for (var symbol in term.symbols) {
var exp = term.symbols[symbol];
exp = (exp !== 1) ? exp : '';
html += symbol + '<sup>' + exp + '</sup>';
}
}
return html;
}
function Term(value) {
this.symbols = {};
if (typeof value==='string') { // Symbol
this.symbols[value] = 1;
this.coeff = 1;
} else if (typeof value==='number') { // Number
this.coeff = value;
} else {
this.coeff = 1;
}
}
Term.same = function(a, b) {
if (Object.keys(a.symbols).length != Object.keys(b.symbols).length)
return false
else
for (var aSymbol in a.symbols)
if (a.symbols[aSymbol] != b.symbols[aSymbol]) return false
return true
}
Term.add = function(a, b) {
var result = new Term();
Object.assign(result.symbols, a.symbols);
result.coeff = a.coeff + b.coeff;
return result
}
Term.multiply = function(a, b) {
var result = new Term();
Object.assign(result.symbols, a.symbols);
for (var symbol in b.symbols) {
if (!(symbol in result.symbols))
result.symbols[symbol] = 0;
result.symbols[symbol] += b.symbols[symbol];
if (result.symbols[symbol] === 0)
delete result.symbols[symbol];
}
result.coeff = a.coeff * b.coeff;
return result
}
function expand(str) {
var result = [];
var parens = findOuterParens(str);
while (parens) {
result = result.concat(parseExpressionStr(str.slice(0,parens.start)));
result.push(expand(str.slice(parens.start+1, parens.end)))
str = str.slice(parens.end+1);
parens = findOuterParens(str);
}
result = result.concat(parseExpressionStr(str));
// Move -s to coefficients
var minus = result.indexOf('-'), minusPoly = new Polynomial(-1);
while (minus !== -1) {
result[minus] = '+';
result[minus+1] = Polynomial.multiply(minusPoly, result[minus+1]);
minus = result.indexOf('-');
}
// Get rid of +s that follow another operator
var plus = result.indexOf('+');
while (plus !== -1) {
if (plus===0 || isOperator(result[plus-1])) {
result.splice(plus--, 1);
}
plus = result.indexOf('+', plus+1);
}
// Convert /s to *s
var divide = result.indexOf('/');
while (divide !== -1) {
result[divide] = '*';
var termsToInvert = result[divide+1].terms;
if (termsToInvert.length > 1)
throw Error('Attempt to divide by a polynomial with more than one term.');
var termToInvert = termsToInvert[0];
for (var symbol in termToInvert.symbols) {
termToInvert.symbols[symbol] = -termToInvert.symbols[symbol];
}
termToInvert.coeff = 1/termToInvert.coeff;
divide = result.indexOf('/');
}
// Convert ^s to *s
var power = result.indexOf('^');
while (power !== -1) {
var exp = result[power+1];
if (exp.terms.length > 1 || Object.keys(exp.terms[0].symbols).length != 0)
throw Error('Attempt to use non-number as an exponent');
exp = exp.terms[0].coeff;
var base = result[power-1];
var expanded = [power-1, 3, base];
for (var i=0; i<exp-1; i++) {
expanded.push('*');
expanded.push(base);
}
result.splice.apply(result, expanded);
power = result.indexOf('^');
}
// Add implicit *s
for (var i=0; i<result.length-1; i++)
if (!isOperator(result[i]) && !(isOperator(result[i+1])))
result.splice(i+1, 0, '*');
// Multiply
var mult = result.indexOf('*');
while (mult !== -1) {
var product = Polynomial.multiply(result[mult-1], result[mult+1]);
result.splice(mult-1, 3, product);
mult = result.indexOf('*');
}
// Add
var add = result.indexOf('+');
while (add !== -1) {
var sum = Polynomial.add(result[add-1], result[add+1]);
result.splice(add-1, 3, sum);
add = result.indexOf('+');
}
result[0].simplify();
return result[0];
}
var problems = ['(x+1)(x+1)/x', '(x+1)^3', '(x^2*x)(x^2)', '(x+1)(x+1)(x+1)',
'(x+1)^2', 'x(x+1)', '1x^4', '(x + (x+2))(x+5)', '3x^0', '2^3',
'(x+2x(x+2(x+1)x))', '(x+1)(x-1)', '(x+1)(y+1)', '(x+y+t)(q+x+7)'];
var solutionHTML = '';
for (var i = 0; i<problems.length; i++) {
solutionHTML += problems[i] + ' => ' + expand(problems[i]).toHtml() + '<br>';
}
document.body.innerHTML = solutionHTML;
</script>
</html>
It outputs:

Related

Using Javascript to do comparison of user inputs

I have the following code that is using 3 columns to check for difference in input values.
The third column difference in my view is generated fine but while viewing it the table is not displaying it.
What is want to do is to compare base text with new text and thrid text input enteries and display the changes to user in inline or side by side format
index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<title>jsdifflib demo</title>
<link rel="stylesheet" type="text/css" href="diffview.css"/>
<script type="text/javascript" src="diffview.js"></script>
<script type="text/javascript" src="difflib.js"></script>
<style type="text/css">
body {
font-size: 12px;
font-family: Sans-Serif,serif;
}
h2 {
margin: 0.5em 0 0.1em;
text-align: center;
}
.top {
text-align: center;
}
.textInput {
display: block;
width: 49%;
float: left;
}
textarea {
width:100%;
height:300px;
}
label:hover {
text-decoration: underline;
cursor: pointer;
}
.spacer {
margin-left: 10px;
}
.viewType {
font-size: 16px;
clear: both;
text-align: center;
padding: 1em;
}
#diffoutput {
width: 100%;
}
</style>
<script type="text/javascript">
function diffUsingJS(viewType) {
"use strict";
var byId = function (id) { return document.getElementById(id); },
base = difflib.stringAsLines(byId("baseText").value),
newtxt = difflib.stringAsLines(byId("newText").value),
thirdText = difflib.stringAsLines(byId("3rdText").value),
sm = new difflib.SequenceMatcher(base, newtxt, thirdText),
opcodes = sm.get_opcodes(),
diffoutputdiv = byId("diffoutput"),
contextSize = byId("contextSize").value;
diffoutputdiv.innerHTML = "";
contextSize = contextSize || null;
diffoutputdiv.appendChild(diffview.buildView({
baseTextLines: base,
newTextLines: newtxt,
thirdTextLines: thirdText,
opcodes: opcodes,
baseTextName: "Base Text",
newTextName: "New Text",
thirdTextName: "Third Text",
contextSize: contextSize,
viewType: viewType
}));
}
</script>
</head>
<body>
<div class="top">
<strong>Context size (optional):</strong> <input type="text" id="contextSize" value="" />
</div>
<div class="textInput">
<h2>Base Text</h2>
<textarea id="baseText" >a</textarea>
</div>
<div class="textInput spacer">
<h2>New Text</h2>
<textarea id="newText">b</textarea>
</div>
<div class="textInput spacer">
<h2>3rd Text</h2>
<textarea id="3rdText">c</textarea>
</div>
<div class="viewType">
<input type="radio" name="_viewtype" id="sidebyside" onclick="diffUsingJS(0);" /> <label for="sidebyside">Side by Side Diff</label>
<input type="radio" name="_viewtype" id="inline" onclick="diffUsingJS(1);" /> <label for="inline">Inline Diff</label>
</div>
<div id="diffoutput"> </div>
</body>
</html>
diffview.js
var diffview = {
/**
* Builds and returns a visual diff view. The single parameter, `params', should contain
* the following values:
*
* - baseTextLines: the array of strings that was used as the base text input to SequenceMatcher
* - newTextLines: the array of strings that was used as the new text input to SequenceMatcher
* - opcodes: the array of arrays returned by SequenceMatcher.get_opcodes()
* - baseTextName: the title to be displayed above the base text listing in the diff view; defaults
* to "Base Text"
* - newTextName: the title to be displayed above the new text listing in the diff view; defaults
* to "New Text"
* - contextSize: the number of lines of context to show around differences; by default, all lines
* are shown
* - viewType: if 0, a side-by-side diff view is generated (default); if 1, an inline diff view is
* generated
*/
buildView: function (params) {
var baseTextLines = params.baseTextLines;
var newTextLines = params.newTextLines;
var thirdTextLines = params.thirdTextLines;
var opcodes = params.opcodes;
var baseTextName = params.baseTextName ? params.baseTextName : "Base Text";
var newTextName = params.newTextName ? params.newTextName : "New Text";
var thirdTextName = params.thirdTextName ? params.thirdTextName: "Third Text"
var contextSize = params.contextSize;
var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0;
if (baseTextLines == null)
throw "Cannot build diff view; baseTextLines is not defined.";
if (newTextLines == null)
throw "Cannot build diff view; newTextLines is not defined.";
if (thirdTextLines == null)
throw "Cannot build diff view; thirdTextLines is not defined.";
if (!opcodes)
throw "Canno build diff view; opcodes is not defined.";
function celt (name, clazz) {
var e = document.createElement(name);
e.className = clazz;
return e;
}
function telt (name, text) {
var e = document.createElement(name);
e.appendChild(document.createTextNode(text));
return e;
}
function ctelt (name, clazz, text) {
var e = document.createElement(name);
e.className = clazz;
e.appendChild(document.createTextNode(text));
return e;
}
var tdata = document.createElement("thead");
var node = document.createElement("tr");
tdata.appendChild(node);
if (inline) {
node.appendChild(document.createElement("th"));
node.appendChild(document.createElement("th"));
node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName + " vs. " + thirdTextName));
} else {
node.appendChild(document.createElement("th"));
node.appendChild(ctelt("th", "texttitle", baseTextName));
node.appendChild(document.createElement("th"));
node.appendChild(ctelt("th", "texttitle", newTextName));
node.appendChild(document.createElement("th"));
node.appendChild(ctelt("th", "texttitle", thirdTextName));
}
tdata = [tdata];
var rows = [];
var node2;
/**
* Adds two cells to the given row; if the given row corresponds to a real
* line number (based on the line index tidx and the endpoint of the
* range in question tend), then the cells will contain the line number
* and the line of text from textLines at position tidx (with the class of
* the second cell set to the name of the change represented), and tidx + 1 will
* be returned. Otherwise, tidx is returned, and two empty cells are added
* to the given row.
*/
function addCells (row, tidx, tend, textLines, change) {
if (tidx < tend) {
row.appendChild(telt("th", (tidx + 1).toString()));
row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
return tidx + 1;
} else {
row.appendChild(document.createElement("th"));
row.appendChild(celt("td", "empty"));
return tidx;
}
}
function addCellsInline (row, tidx, tidx2, textLines, change) {
row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString()));
row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString()));
row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
}
for (var idx = 0; idx < opcodes.length; idx++) {
code = opcodes[idx];
change = code[0];
var b = code[1];
var be = code[2];
var n = code[3];
var ne = code[4];
var rowcnt = Math.max(be - b, ne - n);
var toprows = [];
var botrows = [];
for (var i = 0; i < rowcnt; i++) {
// jump ahead if we've alredy provided leading context or if this is the first range
if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change=="equal") {
var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize);
if (jump > 1) {
toprows.push(node = document.createElement("tr"));
b += jump;
n += jump;
i += jump - 1;
node.appendChild(telt("th", "..."));
if (!inline) node.appendChild(ctelt("td", "skip", ""));
node.appendChild(telt("th", "..."));
node.appendChild(ctelt("td", "skip", ""));
// skip last lines if they're all equal
if (idx + 1 == opcodes.length) {
break;
} else {
continue;
}
}
}
toprows.push(node = document.createElement("tr"));
if (inline) {
if (change == "insert") {
addCellsInline(node, null, n++, newTextLines, change);
} else if (change == "replace") {
botrows.push(node2 = document.createElement("tr"));
if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete");
if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert");
if (n < ne) addCellsInline(node2, null, n++, thirdTextLines, "insert");
} else if (change == "delete") {
addCellsInline(node, b++, null, baseTextLines, change);
} else {
// equal
addCellsInline(node, b++, n++, baseTextLines, change);
}
} else {
b = addCells(node, b, be, baseTextLines, change);
n = addCells(node, n, ne, newTextLines, change);
n = addCells(node, n, ne, thirdTextLines, change);
}
}
for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]);
for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]);
}
tdata.push(node = document.createElement("tbody"));
for (var idx in rows) rows.hasOwnProperty(idx) && node.appendChild(rows[idx]);
node = celt("table", "diff" + (inline ? " inlinediff" : ""));
for (var idx in tdata) tdata.hasOwnProperty(idx) && node.appendChild(tdata[idx]);
return node;
}
};
difflib.js
var __whitespace = {" ":true, "\t":true, "\n":true, "\f":true, "\r":true};
var difflib = {
defaultJunkFunction: function (c) {
return __whitespace.hasOwnProperty(c);
},
stripLinebreaks: function (str) { return str.replace(/^[\n\r]*|[\n\r]*$/g, ""); },
stringAsLines: function (str) {
var lfpos = str.indexOf("\n");
var crpos = str.indexOf("\r");
var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r";
var lines = str.split(linebreak);
for (var i = 0; i < lines.length; i++) {
lines[i] = difflib.stripLinebreaks(lines[i]);
}
return lines;
},
// iteration-based reduce implementation
__reduce: function (func, list, initial) {
if (initial != null) {
var value = initial;
var idx = 0;
} else if (list) {
var value = list[0];
var idx = 1;
} else {
return null;
}
for (; idx < list.length; idx++) {
value = func(value, list[idx]);
}
return value;
},
// comparison function for sorting lists of numeric tuples
__ntuplecomp: function (a, b) {
var mlen = Math.max(a.length, b.length);
for (var i = 0; i < mlen; i++) {
if (a[i] < b[i]) return -1;
if (a[i] > b[i]) return 1;
}
return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1);
},
__calculate_ratio: function (matches, length) {
return length ? 2.0 * matches / length : 1.0;
},
// returns a function that returns true if a key passed to the returned function
// is in the dict (js object) provided to this function; replaces being able to
// carry around dict.has_key in python...
__isindict: function (dict) {
return function (key) { return dict.hasOwnProperty(key); };
},
// replacement for python's dict.get function -- need easy default values
__dictget: function (dict, key, defaultValue) {
return dict.hasOwnProperty(key) ? dict[key] : defaultValue;
},
SequenceMatcher: function (a, b, c, isjunk) {
this.set_seqs = function (a, b, c, ) {
this.set_seq1(a);
this.set_seq2(b);
this.set_seq3(c);
}
this.set_seq1 = function (a) {
if (a == this.a) return;
this.a = a;
this.matching_blocks = this.opcodes = null;
}
this.set_seq2 = function (b) {
console.log("Value in set ", b)
if (b == this.b) return;
this.b = b;
this.matching_blocks = this.opcodes = this.fullbcount = null;
this.__chain_b();
}
this.set_seq3 = function (c) {
console.log("Value in set ", c)
if (c == this.c) return;
this.c = c;
this.matching_blocks = this.opcodes = this.fullbcount = null;
this.__chain_c();
}
this.__chain_c = function () {
console.log("This is ", this.c)
var c = this.c;
var n = c.length;
var c2j = this.c2j = {};
var populardict = {};
for (var i = 0; i < c.length; i++) {
var celt = c[i];
if (c2j.hasOwnProperty(celt)) {
var indices = c2j[elt];
if (n >= 200 && indices.length * 100 > n) {
populardict[celt] = 1;
delete c2j[celt];
} else {
indices.push(i);
}
} else {
c2j[celt] = [i];
}
}
for (var elt in populardict) {
if (populardict.hasOwnProperty(elt)) {
delete c2j[elt];
}
}
var isjunk = this.isjunk;
var junkdict = {};
if (isjunk) {
for (var elt in populardict) {
if (populardict.hasOwnProperty(elt) && isjunk(elt)) {
junkdict[elt] = 1;
delete populardict[elt];
}
}
for (var elt in c2j) {
if (c2j.hasOwnProperty(elt) && isjunk(elt)) {
junkdict[elt] = 1;
delete c2j[elt];
}
}
}
this.iscjunk = difflib.__isindict(junkdict);
this.iscpopular = difflib.__isindict(populardict);
}
this.__chain_b = function () {
var b = this.b;
var n = b.length;
var b2j = this.b2j = {};
var populardict = {};
for (var i = 0; i < b.length; i++) {
var elt = b[i];
if (b2j.hasOwnProperty(elt)) {
var indices = b2j[elt];
if (n >= 200 && indices.length * 100 > n) {
populardict[elt] = 1;
delete b2j[elt];
} else {
indices.push(i);
}
} else {
b2j[elt] = [i];
}
}
for (var elt in populardict) {
if (populardict.hasOwnProperty(elt)) {
delete b2j[elt];
}
}
var isjunk = this.isjunk;
var junkdict = {};
if (isjunk) {
for (var elt in populardict) {
if (populardict.hasOwnProperty(elt) && isjunk(elt)) {
junkdict[elt] = 1;
delete populardict[elt];
}
}
for (var elt in b2j) {
if (b2j.hasOwnProperty(elt) && isjunk(elt)) {
junkdict[elt] = 1;
delete b2j[elt];
}
}
}
this.isbjunk = difflib.__isindict(junkdict);
this.isbpopular = difflib.__isindict(populardict);
}
this.find_longest_match = function (alo, ahi, blo, bhi) {
var a = this.a;
var b = this.b;
var b2j = this.b2j;
var isbjunk = this.isbjunk;
var besti = alo;
var bestj = blo;
var bestsize = 0;
var j = null;
var k;
var j2len = {};
var nothing = [];
for (var i = alo; i < ahi; i++) {
var newj2len = {};
var jdict = difflib.__dictget(b2j, a[i], nothing);
for (var jkey in jdict) {
if (jdict.hasOwnProperty(jkey)) {
j = jdict[jkey];
if (j < blo) continue;
if (j >= bhi) break;
newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1;
if (k > bestsize) {
besti = i - k + 1;
bestj = j - k + 1;
bestsize = k;
}
}
}
j2len = newj2len;
}
while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
besti--;
bestj--;
bestsize++;
}
while (besti + bestsize < ahi && bestj + bestsize < bhi &&
!isbjunk(b[bestj + bestsize]) &&
a[besti + bestsize] == b[bestj + bestsize]) {
bestsize++;
}
while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
besti--;
bestj--;
bestsize++;
}
while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) &&
a[besti + bestsize] == b[bestj + bestsize]) {
bestsize++;
}
return [besti, bestj, bestsize];
}
this.get_matching_blocks = function () {
if (this.matching_blocks != null) return this.matching_blocks;
var la = this.a.length;
var lb = this.b.length;
var queue = [[0, la, 0, lb]];
var matching_blocks = [];
var alo, ahi, blo, bhi, qi, i, j, k, x;
while (queue.length) {
qi = queue.pop();
alo = qi[0];
ahi = qi[1];
blo = qi[2];
bhi = qi[3];
x = this.find_longest_match(alo, ahi, blo, bhi);
i = x[0];
j = x[1];
k = x[2];
if (k) {
matching_blocks.push(x);
if (alo < i && blo < j)
queue.push([alo, i, blo, j]);
if (i+k < ahi && j+k < bhi)
queue.push([i + k, ahi, j + k, bhi]);
}
}
matching_blocks.sort(difflib.__ntuplecomp);
var i1 = 0, j1 = 0, k1 = 0, block = 0;
var i2, j2, k2;
var non_adjacent = [];
for (var idx in matching_blocks) {
if (matching_blocks.hasOwnProperty(idx)) {
block = matching_blocks[idx];
i2 = block[0];
j2 = block[1];
k2 = block[2];
if (i1 + k1 == i2 && j1 + k1 == j2) {
k1 += k2;
} else {
if (k1) non_adjacent.push([i1, j1, k1]);
i1 = i2;
j1 = j2;
k1 = k2;
}
}
}
if (k1) non_adjacent.push([i1, j1, k1]);
non_adjacent.push([la, lb, 0]);
this.matching_blocks = non_adjacent;
return this.matching_blocks;
}
this.get_opcodes = function () {
if (this.opcodes != null) return this.opcodes;
var i = 0;
var j = 0;
var answer = [];
this.opcodes = answer;
var block, ai, bj, size, tag;
var blocks = this.get_matching_blocks();
for (var idx in blocks) {
if (blocks.hasOwnProperty(idx)) {
block = blocks[idx];
ai = block[0];
bj = block[1];
size = block[2];
tag = '';
if (i < ai && j < bj) {
tag = 'replace';
} else if (i < ai) {
tag = 'delete';
} else if (j < bj) {
tag = 'insert';
}
if (tag) answer.push([tag, i, ai, j, bj]);
i = ai + size;
j = bj + size;
if (size) answer.push(['equal', ai, i, bj, j]);
}
}
return answer;
}
// this is a generator function in the python lib, which of course is not supported in javascript
// the reimplementation builds up the grouped opcodes into a list in their entirety and returns that.
this.get_grouped_opcodes = function (n) {
if (!n) n = 3;
var codes = this.get_opcodes();
if (!codes) codes = [["equal", 0, 1, 0, 1]];
var code, tag, i1, i2, j1, j2;
if (codes[0][0] == 'equal') {
code = codes[0];
tag = code[0];
i1 = code[1];
i2 = code[2];
j1 = code[3];
j2 = code[4];
codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2];
}
if (codes[codes.length - 1][0] == 'equal') {
code = codes[codes.length - 1];
tag = code[0];
i1 = code[1];
i2 = code[2];
j1 = code[3];
j2 = code[4];
codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)];
}
var nn = n + n;
var group = [];
var groups = [];
for (var idx in codes) {
if (codes.hasOwnProperty(idx)) {
code = codes[idx];
tag = code[0];
i1 = code[1];
i2 = code[2];
j1 = code[3];
j2 = code[4];
if (tag == 'equal' && i2 - i1 > nn) {
group.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]);
groups.push(group);
group = [];
i1 = Math.max(i1, i2-n);
j1 = Math.max(j1, j2-n);
}
group.push([tag, i1, i2, j1, j2]);
}
}
if (group && !(group.length == 1 && group[0][0] == 'equal')) groups.push(group)
return groups;
}
this.ratio = function () {
matches = difflib.__reduce(
function (sum, triple) { return sum + triple[triple.length - 1]; },
this.get_matching_blocks(), 0);
return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
}
this.quick_ratio = function () {
var fullbcount, elt;
if (this.fullbcount == null) {
this.fullbcount = fullbcount = {};
for (var i = 0; i < this.b.length; i++) {
elt = this.b[i];
fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1;
}
}
fullbcount = this.fullbcount;
var avail = {};
var availhas = difflib.__isindict(avail);
var matches = numb = 0;
for (var i = 0; i < this.a.length; i++) {
elt = this.a[i];
if (availhas(elt)) {
numb = avail[elt];
} else {
numb = difflib.__dictget(fullbcount, elt, 0);
}
avail[elt] = numb - 1;
if (numb > 0) matches++;
}
return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
}
this.real_quick_ratio = function () {
var la = this.a.length;
var lb = this.b.length;
return _calculate_ratio(Math.min(la, lb), la + lb);
}
this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction;
this.a = this.b = null;
this.set_seqs(a, b, c);
}
};

isset equivalent in javascript to find palindrome

I created a script in PHP to find a palindrome, but when I try to do the same in JavaScript, then it is not working as expected. It's not just a matter of checking if the string that is reversed matches, but any order of the string has to be checked as well.
In other words, "mom" should return as true, "mmo" should return as true, "omm" should return as true, etc..., which is what the PHP script does, but the JS script below doesn't even work for the first iteration for the string "mom"
The following is the PHP script:
<?php
function is_palindrom($str) {
$str_array = str_split($str);
$count = array();
foreach ($str_array as $key) {
if(isset($count[$key])) {
$count[$key]++;
} else {
$count[$key] = 1;
}
}
$odd_counter = 0;
foreach ($count as $key => $val) {
if(($val % 2) == 1) {
$odd_counter++;
}
}
return $odd_counter <= 1;
}
echo is_palindrom('mom') ? "true" : "false";
The following is what I have tried in JS:
var count = [];
var strArr = [];
var oddCounter = 0;
var foreach_1 = function(item, index) {
console.log("count[index]: " + count[index]);
if (typeof count[index] !== "undefined") {
count[index]++;
} else {
count[index] = 1;
}
};
var foreach_2 = function(item, index) {
console.log("item: " + item + " item % 2: " + eval(item % 2));
if (eval(item % 2) == 1) {
oddCounter++;
}
console.log("oddCounter: " + oddCounter);
return oddCounter <= 1;
};
var isPalindrom = function(str) {
strArr = str.split("");
console.log(strArr);
strArr.forEach(foreach_1);
console.log(count);
count.forEach(foreach_2);
};
I believe it is failing where I try to replicate isset in javascript, with the following code:
if (typeof count[index] !== "undefined") {
As a result, I have tried to write my own isset function, but still the same result, it is not working:
var isset = function(obj) {
if (typeof obj === "undefined" || obj === null) {
return false;
} else {
return true;
}
};
With the following function being called:
if (isset(count[index])) {
count[index]++;
} else {
count[index] = 1;
}
As usual, any help would be appreciated and thanks in advance
BTW, it's killing me that I cannot remember the word for several revisions or iterations of something - I know that it starts with "re"
My attempt:
let p1 = `No 'x' in Nixon.`
let p2 = `Was it a car or a cat I saw?`
let p3 = `A man, a plan, a canal, Panama!`
function is_palindrome (str) {
const normalize = str => str.replace(/[.,:;`'"!?\/#$%\^&\*{}=\-_~()\s]/g, '').toLowerCase()
const reverse = str => [...str].reverse().join('')
return normalize(str) === reverse(normalize(str))
? true
: false
}
console.log(is_palindrome(p1))
console.log(is_palindrome(p2))
console.log(is_palindrome(p3))
First, thank you for all the comments.
Second, I ran a var_dump on the count array in the PHP file and this was the result:
array (size=2)
'm' => int 2
'o' => int 1
Which lead me to understand that count in js has to be an object for this work and I would have to create indexes of the object, depending on the string entered.
One thing lead to another and a complete re-write, but it works, along with a spell checker - see link at the bottom for complete code:
var count = {};
var strArr = [];
var oddCounter = 0;
var objKeys = [];
var splitString;
var reverseArray;
var joinArray;
var url = "test-spelling.php";
var someRes = "";
var mForN = function(obj, strArr) {
for (var y = 0; y < strArr.length; y++) {
// console.log("obj[strArr[" + y + "]]: " + obj[strArr[y]]);
if (isset(obj[strArr[y]])) {
obj[strArr[y]]++;
} else {
obj[strArr[y]] = 1;
}
}
return obj;
};
var mForN_2 = function(obj, objKeys) {
for (var z = 0; z < objKeys.length; z++) {
/* console.log(
"obj[objKeys[z]]: " +
obj[objKeys[z]] +
" obj[objKeys[z]] % 2: " +
eval(obj[objKeys[z]] % 2)
); */
if (eval(obj[objKeys[z]] % 2) == 1) {
oddCounter++;
}
// console.log("oddCounter: " + oddCounter);
}
return oddCounter <= 1;
};
var isset = function(obj) {
if (typeof obj === "undefined" || obj === null) {
return false;
} else {
return true;
}
};
var isPalindrom = function(str) {
// reverse original string
splitString = str.split("");
reverseArray = splitString.reverse();
joinArray = reverseArray.join("");
var checking = checkSpellingOfStr(str);
if (str == joinArray) {
strArr = str.split("");
// console.log("strArr: " + strArr);
objKeys = makeObjKeys(count, strArr);
// console.log("filled count before mForN: " + JSON.stringify(count));
// create array of keys in the count object
objKeys = Object.keys(count);
// console.log("objKeys: " + objKeys);
count = mForN(count, strArr);
// console.log("count after mForN: " + JSON.stringify(count));
return mForN_2(count, objKeys);
} else {
return 0;
}
};
var makeObjKeys = function(obj, arr) {
for (var x = 0; x < arr.length; x++) {
obj[arr[x]] = null;
}
return obj;
};
var checkSpellingOfStr = function(someStr) {
var formData = {
someWord: someStr
};
$.ajax({
type: "GET",
url: url,
data: formData,
success: function(result) {
if (!$.trim(result)) {
} else {
console.log(result);
$("#checkSpelling").html(result);
}
}
});
};
Start everything with the following call:
isPalindrom("mom") ? demoP.innerHTML = "is pal" : demoP.innerHTML = "is not pal";
In my example, I have a form and I listen for a button click as follows:
var palindromeTxt = document.getElementById("palindromeTxt").value;
var btn = document.getElementById("button");
btn.addEventListener("click", function (event) {
isPalindrom(palindromeTxt) ? demoP.innerHTML = "is pal" : demoP.innerHTML = "is not pal";
});
The following is the php for spell check:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
if(!empty($_REQUEST['someWord']))
{
$someWord = $_REQUEST['someWord'];
}
$pspell_link = pspell_new("en");
if (pspell_check($pspell_link, $someWord)) {
echo trim($someWord) . " is a recognized word in the English language";
} else {
echo "Your word is either misspelled or that is not a recognized word";
}
You will need pspell installed on your server, as well as adding extension=pspell.so to your php.ini
This is what I did, to get it running locally on my mac:
cd /Users/username/Downloads/php-5.6.2/ext/pspell
/usr/local/bin/phpize
./configure --with-php-config=/usr/local/php5-5.6.2-20141102-094039/bin/php-config --with-pspell=/opt/local/
make
cp ./modules/* /usr/local/php5-5.6.2-20141102-094039/lib/php/extensions/no-debug-non-zts-20131226
sudo apachectl restart
check your phpinfo file and you should see the following:
pspell
PSpell Support enabled
Live example

Is there a javascript library that does spreadsheet calculations without the UI

I am working on a project that needs an excel like calculation engine in the browser. But, it doesn't need the grid UI.
Currently, I am able to do it by hiding the 'div' element of Handsontable. But, it isn't elegant. It is also a bit slow.
Is there a client side spreadsheet calculation library in javascript that does something like this?
x = [ [1, 2, "=A1+B1"],
[2, "=SUM(A1,A2"),3] ];
y = CalculateJS(x);
##############
y: [[1, 2, 3],
[2,3,3]]
I'm not aware of any (although I haven't really looked), but if you wish to implement your own, you could do something along these lines (heavily unoptimized, no error checking):
functions = {
SUM: function(args) {
var result = 0;
for (var i = 0; i < args.length; i++) {
result += parseInt(args[i]);
}
return result;
}
};
function get_cell(position) {
// This function returns the value of a cell at `position`
}
function parse_cell(position) {
cell = get_cell(position);
if (cell.length < 1 || cell[0] !== '=')
return cell;
return parse_token(cell.slice(1));
}
function parse_token(tok) {
tok = tok.trim();
if (tok.indexOf("(") < 0)
return parse_cell(tok);
var name = tok.slice(0, tok.indexOf("("));
if (!(name in functions)) {
return 0; // something better than this?
}
var arguments_tok = tok.slice(tok.indexOf("(") + 1);
var arguments = [];
while (true) {
var arg_end = arguments_tok.indexOf(",");
if (arg_end < 0) {
arg_end = arguments_tok.lastIndexOf(")");
if (arg_end < 0)
break;
}
if (arguments_tok.indexOf("(") >= 0 && (arguments_tok.indexOf("(") < arg_end)) {
var paren_amt = 1;
arg_end = arguments_tok.indexOf("(") + 1;
var end_tok = arguments_tok.slice(arguments_tok.indexOf("(") + 1);
while (true) {
if (paren_amt < 1) {
var last_index = end_tok.indexOf(",");
if (last_index < 0)
last_index = end_tok.indexOf(")");
arg_end += last_index;
end_tok = end_tok.slice(last_index);
break;
}
if (end_tok.indexOf("(") > 0 && (end_tok.indexOf("(") < end_tok.indexOf(")"))) {
paren_amt++;
arg_end += end_tok.indexOf("(") + 1;
end_tok = end_tok.slice(end_tok.indexOf("(") + 1);
} else {
arg_end += end_tok.indexOf(")") + 1;
end_tok = end_tok.slice(end_tok.indexOf(")") + 1);
paren_amt--;
}
}
}
arguments.push(parse_token(arguments_tok.slice(0, arg_end)));
arguments_tok = arguments_tok.slice(arg_end + 1);
}
return functions[name](arguments);
}
Hopefully this will give you a starting point!
To test in your browser, set get_cell to function get_cell(x) {return x;}, and then run parse_cell("=SUM(5,SUM(1,7,SUM(8,111)),7,8)"). It should result in 147 :)
I managed to do this using bacon.js. It accounts for cell interdependencies. As of now, it calculates values for javascript formula instead of excel formula by using an eval function. To make it work for excel formulae, all one has to do is replace eval with Handsontable's ruleJS library. I couldn't find a URI for that library... hence eval.
https://jsfiddle.net/sandeep_muthangi/3src81n3/56/
var mx = [[1, 2, "A1+A2"],
[2, "A2", "A3"]];
var output_reference_bus = {};
var re = /\$?[A-N]{1,2}\$?[1-9]{1,4}/ig
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('');
function convertToCellRef(rows, cols) {
var alphabet_index = rows+1,
abet = "";
while (alphabet_index>0) {
abet = alphabet[alphabet_index%alphabet.length-1]+abet;
alphabet_index = Math.floor(alphabet_index/alphabet.length);
}
return abet+(cols+1).toString();
}
function getAllReferences(value) {
if (typeof value != "string")
return null;
var references = value.match(re)
if (references.length == 0)
return null;
return references;
}
function replaceReferences(equation, args) {
var index = 0;
return equation.replace(re, function(match, x, string) {
return args[index++];
});
}
//Assign an output bus to each cell
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
output_reference_bus[convertToCellRef(row_index, cell_index)] = Bacon.Bus();
})
})
//assign input buses based on cell references... and calculate the result when there is a value on all input buses
mx.forEach(function(row, row_index) {
row.forEach(function(cell, cell_index) {
if ((all_refs = getAllReferences(cell)) != null) {
var result = Bacon.combineAsArray(output_reference_bus[all_refs[0]]);
for (i=1; i<all_refs.length; i++) {
result = Bacon.combineAsArray(result, output_reference_bus[all_refs[i]]);
}
result = result.map(function(data) {
return eval(replaceReferences(cell, data));
})
result.onValue(function(data) {
console.log(convertToCellRef(row_index, cell_index), data);
output_reference_bus[convertToCellRef(row_index, cell_index)].push(data);
});
}
else {
if (typeof cell != "string")
output_reference_bus[convertToCellRef(row_index, cell_index)].push(cell);
else
output_reference_bus[convertToCellRef(row_index, cell_index)].push(eval(cell));
}
})
})
output_reference_bus["A2"].push(20);
output_reference_bus["A1"].push(1);
output_reference_bus["A1"].push(50);

Split String into dictionary words and non-dictionary words

I want to split a string into a sequence of words using a dictionary.
The following function only gives me words with letter boundaries.
function spltToWord(prm){
var spltedAr = prm.split(/(?=[A-Z][a-z])/);
return spltedAr.join(" ").trim();
}
I want to separate the text into dictionary/non-dictionary words.
For example:
Iamno123prisoner - I am no 123 prisoner
USAisveryrich - USA is very rich
Albertgaveme5rupees - Albert gave me 5 rupees
A sample dictionary can be found Here for refrence.
If the dictionary contains many words, you'll want a faster solution than matching one word at a time against substrings of the input text. If there are ten thousand words in the dictionary, you have to do at least ten thousand character comparisons with this naive approach.
A more efficient solution involves the use of a trie in which you've stored the dictionary words. Checking whether a word is in the dictionary requires only a single pass through the string.
Another advantage is that you can search for the longest match starting from a given position in the text. This approach is demonstrated in the following snippet, which includes a small dictionary that suffices to process your examples. You can replace this with your own dictionary when you run the code locally.
var Global = {
dictionary: [
'aa', 'I', 'i', 's',
'aah', 'am',
'aahed', 'no',
'aahing', '123',
'aahs', 'prisoner',
'aal', 'USA',
'aalii', 'is',
'aaliis', 'very',
'aals', 'rich',
'aardvark', 'Albert',
'aardvarks', 'gave', 'me', '5', 'rupees'
]
};
function addToTrie(word) {
var node = Global.trie;
for (var pos = 0; pos < word.length; ++pos) {
var letter = word.charAt(pos);
if (node[letter] === undefined) {
node[letter] = {};
}
node = node[letter];
}
node.terminal = true;
}
function findLongestMatch(text, start) {
var node = Global.trie,
best = start - 1;
for (var pos = start; pos < text.length; ++pos) {
var letter = text.charAt(pos);
if (node[letter] === undefined) {
break;
}
node = node[letter];
if (node.terminal) {
best = pos;
}
}
return best - start + 1;
}
function loadTrie() {
var words = Global.dictionary,
trie = Global.trie = {};
words.forEach(function (word) {
addToTrie(word);
});
};
function println(label, s, labelStyle) {
if (s === undefined) {
s = label || '';
} else {
var className = 'label' + (labelStyle ? ' ' + labelStyle : '');
s = '<div class="' + className + '">' + label + '</div> ' + s;
}
document.getElementById('message').innerHTML += (s + '<br />');
}
function process(text) {
var results = [],
letters = [],
pos = 0;
while (pos < text.length) {
var length = findLongestMatch(text, pos);
if (length == 0) {
letters.push(text.charAt(pos));
pos += 1;
} else {
if (letters.length != 0) {
results.push({ word: letters.join(''), known: false });
letters = [];
}
results.push({ word: text.substring(pos, pos + length), known: true });
pos += length;
}
}
if (letters.length != 0) {
results.push({ word: letters.join(''), known: false });
letters = [];
}
var breakdown = results.map(function (result) {
return result.word;
});
println();
println(' breakdown:', '/' + breakdown.join('/') + '/');
results.forEach(function (result) {
println((result.known ? 'in dictionary' : ' unknown') + ':',
'"' + result.word + '"', (result.known ? undefined : 'unknown'));
});
};
window.onload = function () {
loadTrie();
process('WellIamno123prisoner');
process('TheUSAisveryrichman');
process('Albertgaveme5rupeesyo');
};
body {
font-family: monospace;
color: #444;
font-size: 14px;
line-height: 20px;
}
.label {
display: inline-block;
width: 200px;
font-size: 13px;
color: #aaa;
text-align: right;
}
.label.unknown {
color: #c61c39;
}
<div id="message"></div>
Edit #2
var words = ["apple", "banana", "candy", "cookie", "doughnut"];
var input = "banana123candynotawordcookieblahdoughnut";
var currentWord = "",
result = "";
while (true) {
for (var i = 0; i < input.length; i++) {
currentWord += input[i];
if (words.indexOf(currentWord) >= 0) {
result += " " + currentWord + " ";
currentWord = "";
}
}
if (currentWord.length > 0) {
result += currentWord[0];
input = currentWord.substr(1);
currentWord = "";
} else {
break;
}
}
console.log(result.trim()); // "banana 123 candy notaword cookie blah doughnut"
Note the result will contain two spaces if there are two consecutive dictionary words. You can fix this using regex.
It isn't the prettiest solution (you can make it recursive which may be more readable), and it doesn't provide multiple solutions (see DP link in comment for that).
Edit this doesn't take into account non-dictionary words.
Using a simple greedy algorithm:
var words = ["apple", "banana", "candy", "cookie", "doughnut"];
var input = "bananacandycookiedoughnut";
var currentWord = "", result = "";
for (var i = 0; i < input.length; i++) {
currentWord += input[i];
if (words.indexOf(currentWord) >= 0) {
result += currentWord + " ";
currentWord = "";
}
}
console.log(result.trim()); // "banana candy cookie donught"
Of course you would want to modify various part of this, e.g. use a set for the dictionary and take into account case [in]sensitivity.

How to write a javascript function that checks if first and last characters in a string are equal

I been doing this javascript challenge and I'm pretty close, but something is off.Here's the challenge:
Given an array of strings containing three types of braces: round (), square [] and curly {}
Your task is to write a function that checks whether the braces in each string are correctly matched. Prints 1 to standard output (console.log) if the braces in each string are matched and 0 if they're not (one result per line)
my code is this:
var infoToParse = [ ")(){}", "[]({})", "([])", "{()[]}", "([)]" ];
function checkBraces(infoToParse) {
var tabChars = infoToParse;
for (i= 0; tabChars.length - 1; i+=1) {
if (tabChars[i].charAt(0) === tabChars[i].charAt(tabChars[i].length-1)){
console.log(1);
}else{
console.log(0);
}
}
}
checkBraces(infoToParse);
The output with the current array items should be Output:
0
1
1
1
0
As pointed out in the comment, only having the first and the last character same would not result in a correct solution.
You can try the following technique:
Maintain a stack, each time you encounter an opening bracket i.e round "(", square "[" or curly "{"; push this into stack. Now whenever you encounter a closing bracket, pop an element from the stack. If these two match i.e both are of same type, then carry on till stack and string both are empty. If at any point these don't match then break and return false.
I'll write a code for it and post it soon.
I guess you could do it in this way, keeping a "tree" of starting positions. Didn't test any other testcases than your own though :)
var testCases = [")(){}", "[]({})", "([])", "{()[]}", "([)]"];
var braceType = {
round: ["(", ")"],
curly: ["{", "}"],
square: ["[", "]"]
};
var bracePosition = {
start: ["{", "(", "["],
end: ["}", ")", "]"]
};
function typeOfBraces(sign) {
for (var property in braceType) {
if (braceType[property].indexOf(sign) < 0) {
continue;
}
if (bracePosition.start.indexOf(sign) < 0) {
return {
type: property,
position: "end"
};
} else {
return {
type: property,
position: "start"
};
}
}
throw "Sign is not a brace!";
};
function Braces(brace, parent, type) {
this.brace = brace;
this.parent = parent || null;
this.type = type || {
type: 'init',
position: ''
};
this.children = [];
this.nextBrace = function(nextSign) {
var nextType = typeOfBraces(nextSign);
if (nextType.position === 'start') {
var child = new Braces(nextSign, this, nextType);
this.children.push(child);
return child;
}
if (nextType.position === 'end') {
if (this.type.position === '') {
throw 'Cannot start with an end tag!';
}
if (this.type.position === 'end' && this.parent === null) {
throw 'Cannot end the sequence';
}
if (this.type.position === 'end' && this.parent.position === 'start') {
if (this.type.type === this.parent.type) {
var child = new Braces(nextSign, this.parent, nextType);
this.parent.children.add(child);
return this.parent;
}
}
}
if (this.type.position === 'start' && nextType.type === this.type.type && nextType.position === 'end') {
return this.parent;
}
return new Braces(nextSign, this, nextType);
};
}
for (var i = 0; i < testCases.length; i++) {
var brace = new Braces(testCases[i]);
for (var j = 0, len = testCases[i].length; j < len; j++) {
try {
brace = brace.nextBrace(testCases[i][j]);
} catch (e) {
console.log(e);
brace = null;
break;
}
}
if (brace != null && brace.parent == null) {
// valid entry
console.log(brace);
console.log(testCases[i] + " is a valid sequence");
} else {
// invalid entry
console.log(testCases[i] + " is an invalid sequence");
}
}
or, to make it a bit easier and to check check the brackets:
function validBraces(braceSequence) {
var stack = '',
i, len, lastStack = -1,
toAdd = "{([",
toRemove = "})]",
sign;
for (i = 0, len = braceSequence.length; i < len; i++) {
sign = braceSequence[i];
if (toAdd.indexOf(sign) >= 0) {
stack += sign;
lastStack++;
} else if (toRemove.indexOf(sign) >= 0) {
if (toAdd.indexOf(stack.charAt(lastStack)) !== toRemove.indexOf(sign)) {
// format exception
console.warn('Format exception, didn\'t expect ' + sign + ' (current stack: ' + stack + ')');
return false;
} else {
stack = stack.slice(0, -1);
lastStack--;
}
} else {
console.warn('Invalid character exception, didn\'t expect ' + sign + ' (current stack: ' + stack + ')');
return false;
}
}
return true;
}
var testCases = [")(){}", "[]({})", "([])", "{()[]}", "([)]"];
for (var i = 0; i < testCases.length; i++) {
if (validBraces(testCases[i])) {
console.log(testCases[i] + ' is a valid sequence');
} else {
console.log(testCases[i] + ' is an invalid sequence');
}
}
If you can use Regular Expressions, you can really slim it down:
var stringArray = [ ")(){}", "[]({})", "([])", "{()[]}", "([)]" ];
function checkBraces(infoToParse) {
for (i = 0; i < infoToParse.length; i += 1) {
var regX = /^\[.*\]$|^\{.*\}$|^\(.*\)$/gi;
var str = infoToParse[i];
console.log(str.match(regX) ? 1 : 0);
}
}
checkBraces(stringArray);
Also, as I stated in my comment, your for syntax was off. Oh, and instead of i+=1, you can use i++ to simplify it.

Categories

Resources