How to fix issue in my "isParenthesisValid" algorithm code - javascript

I am solving an algorithm question whereby it requires me to ensure that brackets, parenthesis and braces are put in the correct order or sequence.
Here is a link to the question, https://leetcode.com/problems/valid-parentheses/
An example is shown below:
Here is my code for the solution:
const isParenthesisValid = (params) => {
myList = []
lastElement = myList[myList.length - 1]
for (let i = 0; i < params.length; i++) {
if (params[i] === "(" || params[i] === "[" || params[i] === "{" ) {
myList.push(params[i])
} else if ((params[i] === ")" && lastElement === "(") || (params[i] === "]" && lastElement === "[") || (params[i] === "}" && lastElement === "{")) {
myList.pop()
} else return false
}
return myList.length ? false : true
}
// I get false as an answer everytime whether the pattern is correct or wrong
// false
console.log(isParenthesisValid("[()]"))
But I don't know why I get false everytime, I have compared my answer with someones else answer who did the same thing but it seems I am omitting something not so obvious.
I hope someone can point out in my code where i am getting it wrong.

Your lastElement retrieves the last element of the list at the start of the program - when there is no such element - so it's always undefined. You need to retrieve the value inside the loop instead.
const isParenthesisValid = (params) => {
myList = []
for (let i = 0; i < params.length; i++) {
const lastElement = myList[myList.length - 1]
if (params[i] === "(" || params[i] === "[" || params[i] === "{" ) {
myList.push(params[i])
} else if ((params[i] === ")" && lastElement === "(") || (params[i] === "]" && lastElement === "[") || (params[i] === "}" && lastElement === "{")) {
myList.pop()
} else return false
}
return myList.length ? false : true
}
console.log(isParenthesisValid("[()]"))
Or, a bit more readably:
const isParenthesisValid = (input) => {
const openDelimiters = [];
for (const delim of input) {
const lastElement = openDelimiters[openDelimiters.length - 1];
if (delim === "(" || delim === "[" || delim === "{") {
openDelimiters.push(delim)
} else if ((delim === ")" && lastElement === "(") || (delim === "]" && lastElement === "[") || (delim === "}" && lastElement === "{")) {
openDelimiters.pop()
} else return false
}
return openDelimiters.length === 0;
}
console.log(isParenthesisValid("[()]"))
Another approach, linking each delimiter with an object:
const delims = {
')': '(',
'}': '{',
']': '[',
};
const isParenthesisValid = (input) => {
const openDelimiters = [];
for (const delim of input) {
if ('([{'.includes(delim)) {
openDelimiters.push(delim)
} else if (')]}'.includes(delim) && openDelimiters[openDelimiters.length - 1] === delims[delim]) {
openDelimiters.pop()
} else return false
}
return openDelimiters.length === 0;
}
console.log(isParenthesisValid("[()]"))

Related

React Calculator: How to prevent multiple decimals in a number using Math-expression-evaluator?

As the title says I am using a math-expression library to do my calculations but I am having trouble figuring out the logic for decimals and how to prevent two decimals in one number inside an array. I have highlighted the part that is giving me trouble.
Here is my code sandbox link https://codesandbox.io/s/youthful-frost-2s6v6?file=/src/HookClaculator.js:181-2221
const [input, setInput] = useState([0]);
const [notANum, setNotANum] = useState("");
const [AC, setAC] = useState("");
const handleInputOneClick = (e) => {
let checkInput = input;
checkInput = [...checkInput, parseInt(e, 10)];
console.log(checkInput);
if (checkInput[1] !== 0) {
setInput([...input, parseInt(e, 10)]);
if (input.length > 23) {
console.log("exceeded 23");
setInput(["MAX DIGIT LIMIT REACHED"]);
setTimeout(() => {
setInput([0]);
}, 500);
}
}
};
const handleSymbolClick = (e) => {
if (Number.isInteger(input[input.length - 1])) {
setInput([...input, e]);
} else if (input[input.length - 1] === ".") {
setInput([...input, e]);
} else if (typeof input[input.length - 1] === "string") {
input.pop();
setInput([...input, e]);
}
};
const handleDecimalClick = (e) => {
if (input[input.length - 1] === ".") {
setInput([...input]);
} else if (isNaN(input[input.length - 1])) {
setInput([...input, e]);
} **else if (Number.isInteger(input[input.length - 1])) {
setInput([...input, e]);
}**
};
const handleEqualsClick = (e) => {
let exp = "";
if (input[0] === 0 && input.length <= 1) {
console.log("hell yeah");
setNotANum(["NaN"]);
} else if (input.length > 1) {
console.log("input length is " + input.length);
input.forEach((el) => {
return (exp += el);
});
let value = mexp.eval(exp);
setInput([value]);
}
};
const handleClickReset = (e) => {
setAC(e);
setInput([0]);
};
return (
<div id="calculator-div">
<HookDisplay input={input} notANum={notANum} AC={AC} />
<HookButtons
inputOneClick={handleInputOneClick}
symbolClick={handleSymbolClick}
decimalClick={handleDecimalClick}
handleEqualsButtonClick={handleEqualsClick}
handleClickReset={handleClickReset}
/>
</div>
);
};
return (
<div id="calculator-div">
<HookDisplay input={input} notANum={notANum} AC={AC} />
<HookButtons
inputOneClick={handleInputOneClick}
symbolClick={handleSymbolClick}
decimalClick={handleDecimalClick}
handleEqualsButtonClick={handleEqualsClick}
handleClickReset={handleClickReset}
/>
</div>
);
};
I think before pushing the decimal to the array you can ensure if it'll create a valid expression.
As currently the calculator supports basic operations, a number can never have more than one decimal and operands can only be one more than than the operators, leveraging the stated fact. This condition will work (inside handleDecimalClick):
let lastIndex = -1;
for (let i = input.length - 1; i >= 0; i--) {
if (input[i] === ".") {
lastIndex = i;
} else if (
input[i] === "+" ||
input[i] === "-" ||
input[i] === "*" ||
input[i] === "/"
) {
break;
}
}
if (lastIndex !== -1) return;
Also, for extra protection, added a regex to check if the expression is valid before it goes for evaluation (added the below lines in handleEquals):
const inp = input.join("");
const regex = /(?:(?:^|[-+_*/])(?:\s*-?\d+(\.\d+)?(?:[eE][+-]?\d+)?\s*))+$/;
if (!regex.test(inp)) {
return;
}
I forked your codesandbox and added the fix.

How can you filter out related words in large list quickly

I am running this code on ~479k words and it is taking a very long time:
const fs = require('fs')
const words = fs.readFileSync('dicts/eng.csv', 'utf-8')
.split(/\n/)
.filter(x => x && !x.match('-') && !x.match(/[A-Z]/))
.reduce((m, x) => {
m[x] = true
return m
}, {})
for (let word in words) {
for (let form in words) {
if (form.indexOf(word) == 0) {
if (form.length == word.length + 2 && form.endsWith('ed')) {
delete words[form]
} else if (form.length == word.length + 4 && form.endsWith('tion')) {
delete words[form]
} else if (form.length == word.length + 3 && form.endsWith('ing')) {
delete words[form]
} else if (form.length == word.length + 2 && form.endsWith('er')) {
delete words[form]
} else if (form.length == word.length + 2 && form.endsWith('or')) {
delete words[form]
} else if (form.length == word.length + 3 && form.endsWith('est')) {
delete words[form]
} else if (form.length == word.length + 1 && form.endsWith('s')) {
delete words[form]
} else if (form.length == word.length + 3 && form.endsWith('ers')) {
delete words[form]
} else if (form.length == word.length + 4 && form.endsWith('ings')) {
delete words[form]
}
}
}
}
fs.writeFileSync('dicts/eng.out.csv', Object.keys(words).sort().join('\n'))
How can I speed this up to take only a fraction of the time, on the order of a second or two or whatever is more realistic?
Is there some data structure I need to convert this list into that is better suited to a faster algorithm?
this may work:
const fs = require('fs')
let words = fs.readFileSync('dicts/eng.csv', 'utf-8')
.split(/\n/)
.filter(x => x && !x.match('-') && !x.match(/[A-Z]/)).sort()
let out = new Set, root = words[0];
for (let word of words) {
if (!word.startsWith(root))
root = word;
word = root === word ? root : word
.replace(/(ed|tion|ing|er|or|est|s|ers|ings)$/, '')
out.add(word);
}
fs.writeFileSync('dicts/eng.out.csv', [...out].join('\n'));

How to check if the tiles are already filled with values in JavaScript?

So I have this problem solving how to see when tie happens, when the X wins and when the O wins.
Here's the code to determine the value of my tiles.
Javascript code
let winner;
let winner2;
function checkValueForX() {
if (
board[0].textContent === 'X' &&
board[1].textContent === 'X' &&
board[2].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[3].textContent === 'X' &&
board[4].textContent === 'X' &&
board[5].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[6].textContent === 'X' &&
board[7].textContent === 'X' &&
board[8].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[0].textContent === 'X' &&
board[4].textContent === 'X' &&
board[8].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[2].textContent === 'X' &&
board[4].textContent === 'X' &&
board[6].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[0].textContent === 'X' &&
board[3].textContent === 'X' &&
board[6].textContent === 'X'
) {
let winner = 'X';
return true;
} else if (
board[2].textContent === 'X' &&
board[5].textContent === 'X' &&
board[8].textContent === 'X'
) {
let winner = 'X';
return true;
} else {
return false;
}
}
function checkValueForO() {
if (
board[0].textContent === 'O' &&
board[1].textContent === 'O' &&
board[2].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[3].textContent === 'O' &&
board[4].textContent === 'O' &&
board[5].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[6].textContent === 'O' &&
board[7].textContent === 'O' &&
board[8].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[0].textContent === 'O' &&
board[4].textContent === 'O' &&
board[8].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[2].textContent === 'O' &&
board[4].textContent === 'O' &&
board[6].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[0].textContent === 'O' &&
board[3].textContent === 'O' &&
board[6].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else if (
board[2].textContent === 'O' &&
board[5].textContent === 'O' &&
board[8].textContent === 'O'
) {
let winner2 = 'O';
return true;
} else {
return false;
}
}
Here's my function to check if a player win or if a tie happens:
function checkWinOrTie() {
if (winner === true) {
displayWinnwer.innerHTML = 'Winner is ' + activePlayer1 + '!';
} else if (winner2 === true) {
displayWinnwer.innerHTML = 'Winner is ' + activePlayer2 + '!';
}
}
I know that there's still something missing here, can you help me to declare how to detect if there's a winner or tie?
PS. Newbie in JavaScript here pardon me.
You can't have two winners, right?
You can rewrite your checkWinOrTie() function like this:
function checkWinOrTie(){
if (checkValueForX()){
/* are you aware that you misspelled "displayWinnwer"? */
displayWinnwer.innerHTML = 'Winner is ' + activePlayer1 + '!';
} else if(checkValueForO()){
displayWinnwer.innerHTML = 'Winner is ' + activePlayer2 + '!';
} else if (isTie()) {
displayWinnwer.innerHTML = 'There was a tie!';
}
}
Run checkWinOrTie() each time a player completes a move.
And then write the isTie() function like this:
function isTie() {
for (let i = 0; i < board.length; i++) {
//this "if" check assumes your textContent would be "falsy" for boxes that haven't been filled yet
if (!board[i].textContent) {
return false; //because we found at least one empty box
}
}
//at this point, code iterated over every box and did
//not find a single empty box, so the board must be full
return true;
}
Your code could be improved in a lot of ways. But since you're asking specifically how to solve the problem with the approach you already established, this should do the trick.
I have already done something similar ...
find! : How can I change the value of the button when clicked?
well, the approach is different, so here it is:
const board =
[ '-','-','-' // 0 1 2
, '-','-','-' // 3 4 5
, '-','-','-' // 6 7 8
]
function check_LCD(pCod) {
// check for Lines
for(l=0;l<9; l+=3)
{ if (board[l]===pCod && board[l+1]===pCod && board[l+2]===pCod) return true }
// check for Columns
for(c=0;l<4; c++)
{ if (board[c]===pCod && board[c+3]===pCod && board[c+6]===pCod) return true }
// check for Diagonals
return ( (board[0]===pCod && board[4]===pCod && board[8]===pCod)
|| (board[2]===pCod && board[4]===pCod && board[6]===pCod) )
}
function checkWinOrTie()
{
winner_X = check_LCD('X')
winner_O = check_LCD('O')
if (winner_X) {
displayWinnwer.textContent = 'Winner is ' + activePlayer1 + '!';
}
else if(winner_O) {
displayWinnwer.textContent = 'Winner is ' + activePlayer2 + '!';
}
}
using an array is less practical.

Adding conditions to filter() dynamically in Javascript

I want to add conditions in JavaScript filter() method dynamically.
I have the code below:
let condition = '';
let a = ['empEmail', 'employeeAction', 'transactionNo', 'deviceListName', 'projectName'];
if (this.selectedEmployeeAlias != undefined) {
condition += '&& a => a.empEmail === this.selectedEmployeeAlias';
}
if (this.employeeStatusList != undefined) {
condition += '&& a.employeeAction === this.employeeStatusList'
}
if (this.selectedTransactionNo != undefined) {
condition += '&& a.transactionNo === this.selectedTransactionNo';
}
if (this.selectedDeviceList != undefined) {
condition += ' && a.deviceListName == this.selectedDeviceList';
}
if (this.selectedProjectName != undefined) {
condition += '&& a.projectName == this.selectedProjectName';
}
var finalCondition = condition.substring(2, condition.length);
var fArray = arrayDetails.filter(finalCondition);
The code is returning an error as:
finalCondition is not a function.
Could you please let me know how can I add conditions to filter() dynamically.
You could take an array of functions with conditions. Then iterate with every.
var conditions = [];
if (this.selectedEmployeeAlias !== undefined) {
conditions.push(a => a.empEmail === this.selectedEmployeeAlias);
}
if (this.employeeStatusList !== undefined) {
conditions.push(a => a.employeeAction === this.employeeStatusList);
}
if (this.selectedTransactionNo !== undefined) {
conditions.push(a => a.transactionNo === this.selectedTransactionNo);
}
if (this.selectedDeviceList !== undefined) {
conditions.push(a => a.deviceListName == this.selectedDeviceList);
}
if (this.selectedProjectName !== undefined) {
conditions.push(a => a.projectName == this.selectedProjectName);
}
var fArray = arrayDetails.filter(o => conditions.every(c => c(o)));
As you got the nakes of the keys, just loop over them and check for undefineds:
const keys = ['empEmail', 'employeeAction', 'transactionNo', 'deviceListName', 'projectName'];
const result = arrayDetails.filter(el => {
for(const key of keys) {
if(this[key] === undefined) continue;
if(this[key] !== el[key]) return false;
}
return true;
});
eval to the Rescue!
While it's generally advised against, eval does exactly what you want here.
Just pass your condition variable to eval inside the .filter method and voila!
let condition='';
let a = ['empEmail', 'employeeAction', 'transactionNo', 'deviceListName', 'projectName'];
if (this.selectedEmployeeAlias != undefined) {
condition += '&& a => a.empEmail === this.selectedEmployeeAlias';
}
if (this.employeeStatusList != undefined) {
condition += '&& a.employeeAction === this.employeeStatusList'
}
if (this.selectedTransactionNo != undefined) {
condition += '&& a.transactionNo === this.selectedTransactionNo';
}
if (this.selectedDeviceList != undefined) {
condition += ' && a.deviceListName == this.selectedDeviceList';
}
if (this.selectedProjectName != undefined) {
condition += '&& a.projectName == this.selectedProjectName';
}
var finalCondition=condition.substring(2, condition.length);
var fArray=arrayDetails.filter(stuff => eval(finalCondition));

toTitleCase function not working correctly

I am trying to convert this string into the proper case, but it will not return the correct case. Any idea what is going wrong? (I get no errors).
var convert = "this is the end";
String.prototype.toTitleCase = function () {
var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;
return this.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function (match, index, title) {
if (index > 0 && index + match.length !== title.length &&
match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
(title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
title.charAt(index - 1).search(/[^\s-]/) < 0) {
return match.toLowerCase();
}
if (match.substr(1).search(/[A-Z]|\../) > -1) {
return match;
}
return match.charAt(0).toUpperCase() + match.substr(1);
});
};
convert.toTitleCase();
alert(convert);
This line convert.toTitleCase(); is throwing away the result. The method is returning the correct result, but you aren't doing anything with it.
var original = "this is the end";
String.prototype.toTitleCase = function () {
var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;
return this.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function (match, index, title) {
if (index > 0 && index + match.length !== title.length &&
match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
(title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
title.charAt(index - 1).search(/[^\s-]/) < 0) {
return match.toLowerCase();
}
if (match.substr(1).search(/[A-Z]|\../) > -1) {
return match;
}
return match.charAt(0).toUpperCase() + match.substr(1);
});
};
var titleCased = original.toTitleCase();
alert(titleCased);

Categories