I am trying to write a function that checks if a syntax is correct or not. If it is correct it returns 'ok' else it returns the index of the error. So far my code works if it is correct, if the error is at the first index or last index. Finding errors inbetween is what i am finding difficult. Here is my code.
function syntaxError(syntax) {
let arr = syntax.split('').join()
let arr1 = [];
let arr2 = [];
let result;
//Error if the first index contain a closing braces
if (arr[0] === '>' || arr[0] === ']' || arr[0] === '}' || arr[0] === ')') {
result = 0
};
if (arr === "") {
result = 'ok'
};
//Error if its just a single brace
if (arr.length === 1) {
result = 0
};
//Error if the last index contain an opening braces
if (arr.slice(-1) === '<' || arr.slice(-1) === '[' || arr.slice(-1) === '{' || arr.slice(-1) === '(') {
result = indexOf(arr.slice(-1))
};
let char = arr[i];
if (char == '[' || char == '{' || char == '<' || char == '(') {
arr1.push(char)
} else {
arr2.push(char);
}
if (arr1.length === 0 || arr2.length === 0) {
result = 0
}
if (arr1.length === arr2.length) {
result = 'ok'
}
return result
}
The example below should return 95
('[[[[[[[[[[[[[[]]]]]]]]<<<<<<<<<<<>>>>>>>>>>>]]]]]]'+'[[[[[[[[[[[[[[]]]]]]]
<<<<<<<<<<<>>>>>>>>>>>]}]]]]' + '>')
You could take a an array for the index of each opening character and pop this if the related closing character is found.
If finished and the stack has no item, the syntax is ok, otherwise return an index or the index of the last pushed opening character.
Example:
code comment
----------- ---------------------------
[][[[]][][]
[] balanced
[ error, this returns later 2
[[]] balanced
[] balanced
[] balanced
finally a missing ]
function syntaxError(syntax) {
const
isOpening = c => /[<[{(]/.test(c),
isClosing = c => /[>\]})]/.test(c),
open = { '>': '<', ']': '[', '}': '{', ')': '(' };
var stack = [],
index,
finished = Array
.from(syntax)
.every((c, i) => {
var temp = stack[stack.length - 1];
if (isOpening(c)) {
if (temp && temp.c === c) {
temp.indices.push(i);
} else {
stack.push({ c, indices: [i] });
}
return true;
}
if (isClosing(c)) {
if (temp && temp.c === open[c]) {
temp.indices.pop();
if (!temp.indices.length) stack.pop();
} else {
index = stack.length ? stack.pop().indices.pop() : i;
return false;
}
}
return true;
});
return finished && !stack.length
? 'ok'
: index === undefined
? stack.pop().indices.pop()
: index;
}
console.log(syntaxError('[][][[{}]]')); // ok
console.log(syntaxError(')'));
// 0
console.log(syntaxError('[][][[{<}]]'));
// 01234567
console.log(syntaxError('[][[[]][][]'));
// 012
console.log(syntaxError('[[[[[[[[[[[[[[]]]]]]]]<<<<<<<<<<<>>>>>>>>>>>]]]]]]'+'[[[[[[[[[[[[[[]]]]]]]<<<<<<<<<<<>>>>>>>>>>>]}]]]]' + '>'));
An algorithm for doing this is, find an opening symbol, push it to a stack (or array, whatever). Find another opening symbol, push it to a stack. Find a closing symbol that matches the top of the stack, pop the opening symbol off the stack. Find a closing symbol that doesn't match the top of the stack, you found an error. Find a closing symbol and there's nothing on the stack, you found an error. Get to the end and still have symbols on the stack, you found an error.
Related
Function must return true for "()" sequence and false for "[)" sequence, so it does. But why this function doesn't return true for "||" sequence? Could you help, please?
I wrote this code, but nothing works :(
function check(s) {
const brackets = {
")": "(",
"]": "[",
"}": "{",
"|": "|",
};
const st = [];
for (let i = 0; i < s.length; i++) {
if (isClosedBracket(s[i])) {
if (brackets[s[i]].toString() !== st.pop()) {
return false;
}
} else {
st.push(s[i]);
}
}
return st.length === 0;
}
//if bracket is in this array, function returns true, so bracket is closing
function isClosedBracket(ch) {
return [")", "]", "}", "|"].indexOf(ch) > -1;
}
Well, since the pipe sequence "||" uses the same character for opening and closing, then when the first | (the opening one) will be encountered, the block of code for the closing bracket will be executed.
Your code works, but it will not operate correctly if the opening character is the same as the closing character. If you absolutely need this feature, then consider adding more checks in this particular situation.
However, the code will get much more complex since a singular | could also mean a closing bracket in an error sequence, so inputs like "(|)" will be a bit complicated to handle.
Thank you all! I solved this problem by adding another if/else block. Here is solution, if you need it =3
function check(str) {
let stack = [];
for (let i = 0; i < str.length; i++) {
if (
+str[i] === 1 ||
+str[i] === 3 ||
+str[i] === 5 ||
str[i] === "(" ||
str[i] === "[" ||
str[i] === "{"
) {
stack.push(str[i]);
} else if (
(+str[i] === 2 && stack.at(-1) === "1") ||
(+str[i] === 4 && stack.at(-1) === "3") ||
(+str[i] === 6 && stack.at(-1) === "5") ||
(str[i] === ")" && stack.at(-1) === "(") ||
(str[i] === "]" && stack.at(-1) === "[") ||
(str[i] === "}" && stack.at(-1) === "{")
) {
stack.pop();
} else if (str[i] !== stack.at(-1)) {
stack.push(str[i]);
} else {
stack.pop();
}
}
return !stack.length > 0;
}
Any ideas how to improve this code?
I'm trying to figure out valid parentheses problem from leetcode using JavaScript and I couldn't figure out a plan on how to solve this problem.
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
An input string is valid if:
Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.
Every close bracket has a corresponding open bracket of the same type.
Example 1:
Input: s = "()"
Output: true
Example 2:
Input: s = "()[]{}"
Output: true
Example 3:
Input: s = "(]"
Output: false
My current thinking process is like this:
Split the string into an array (example: "{}" --> ["{","}", "[", "]", "(", ")"]
Loop through the array
Use the index of each characters to compare...?
Not sure after this...
Help please.
Here's a simple stack implementation:
const BRACKETS = [['(', ')'], ['[', ']'], ['{', '}']];
const OPEN = BRACKETS.reduce((a, [o, c]) => ({...a, [o]: c}), {});
const CLOSE = BRACKETS.reduce((a, [o, c]) => ({...a, [c]: o}), {});
const isBalanced = (s) => {
const stack = [];
for (const c of [...s]) {
if (c in OPEN) stack.push(c);
if (c in CLOSE && stack.pop() !== CLOSE[c]) return false;
}
return !stack.length;
};
console.log(isBalanced('{{[()]()}}'));
console.log(isBalanced('{[)}'));
I first create two lookup objects for opening and closing brackets. Then it's just a matter of looping over the characters, and:
if it's an opening bracket, push it onto the stack;
if it's a closing bracket, pop the last value off the stack and check whether it matches the closing bracket;
after everything is processed, check that the stack is empty.
const OPENING_BRACKETS = ['(', '[', '{']
const CLOSING_BRACKETS = [')', ']', '}']
const hasBalancedBrackets = text => !countUnmatchedBrackets(text)
function countUnmatchedBrackets(text) {
return [...text].filter(isBracket).reduce((stack, bracket) =>
isOpen(bracket) || !isMatch(stack.at(-1), bracket)
? /* push */ stack.concat(bracket)
: /* pop */ stack.slice(0, stack.length - 1), []).length
}
function isMatch(lastBracket, bracket) {
return OPENING_BRACKETS.some((openBracket, i) =>
lastBracket === openBracket &&
bracket === CLOSING_BRACKETS[i])
}
function isBracket(char) { return isOpen(char) || CLOSING_BRACKETS.includes(char) }
function isOpen(bracket) { return OPENING_BRACKETS.includes(bracket) }
[
[false, '[){'],
[true, 'a()a'],
[true, '{{[([a]a)a]a}}'],
[false, 'aa{a}a[aa(a](({[{a[[[()(([[a']
].forEach(([expected, input]) =>
console.assert(expected === hasBalancedBrackets(input), input))
Valid Parentheses In Javascript (Asked in many interviews. Simple and accepted solutions.)
Methods used here are string iteration and charAt and replace methods.
var isValid = function(s) {
let par ={'(' :')','{': '}', '[':']'};
for(let i=0;i<s.length;++i) {
if(s.charAt(i+1) && s.charAt(i+1)== par[s.charAt(i)]){
s = s.replace(s.charAt(i)+s.charAt(i+1),'');
i=i-2;
}
}
if(s.length){
console.log(false)
return false;
} else {
console.log(true)
return true;
}
};
console.log(isValid("()[]{}"))
const isValid = (s) => {
let stack = [];
const map = new Map([
['(', ')'],
['{', '}'],
['[', ']'],
]);
for (let i = 0; i < s.length; i++) {
if (s[i] == '(' || s[i] == '{' || s[i] == '[') {
stack.push(s[i]);
} else {
const last = stack.pop();
if (s[i] !== map.get(last)) {
return false;
}
}
}
return stack.length === 0;
};
Below is my code, it works for some strings but not for all.
Ex: "()()()()()((" expected is false, my code returns true.
function validParentheses(parens){
var stack = [];
parens.split('').map((cur, index) =>{
if(stack.length === 0 || stack[index-1] === cur) stack.push(cur);
else stack.pop();
});
return stack.length > 0 ? false : true;
}
stack[index - 1] will be valid so long as you push every iteration. In the case that you pop an element, the incrementing index will always be out of bounds.
Change it to stack.length - 1 to always get the last element, regardless of what is pushed or popped.
For every '(' there must be a exactly one ')'. So you need a counter to see that there is an exact match
function validParentheses(parens){
const chars = parens.split('');
const numChars = chars.length;
let ii;
let numOpenParens = 0;
for (ii = 0; ii < numChars; ii += 1) {
curChar = chars[ii];
numOpenParens += curChar == '(' ? 1 : -1;
// return false if there is one too many closed parens
if (numOpenParens < 0) {
return false;
}
}
// return true only if all parens have been closed
return numOpenParens === 0;
}
For case when stack's length is greater than 0:
if top of the stack is equal to current iterated parenthesis, push that to stack
else pop the stack
function validParentheses(parens) {
var stack = []
parens.split("").forEach((cur) => {
if (stack.length > 0) {
if (stack[stack.length - 1] === cur) {
stack.push(cur)
} else {
stack.pop()
}
} else {
stack.push(cur)
}
})
return stack.length > 0 ? false : true
}
console.log(validParentheses("()()()()()(("))
console.log(validParentheses("()()()()()()"))
console.log(validParentheses("((()))"))
console.log(validParentheses("((())))"))
in stack[index-1] === cur
you are comparing if the char isn't the same like the one stored in the stack, so )( opposite parens will be valid
you can try do something like this
function validParentheses(parens) {
if (parens % 2 == 1) return false;
for (let i = 0; i < parens.length; i++) {
const char = parens[i];
if (char == "(") {
if (parens[i + 1] == ")") {
i++;
} else {
return false
}
} else {
return false
}
}
return true;
}
You need to check the last added value as well, because an unresolves closing bracket should remain in he stack.
BTW, Array#forEach is the method of choice, because Array#map returns a new array, which is not used here.
function validParentheses(parens) {
var stack = [];
parens.split('').forEach((cur, index) => {
if (cur === ')' && stack[stack.length - 1] === '(') stack.pop();
else stack.push(cur);
});
return !stack.length;
}
console.log(validParentheses("(())()"));
console.log(validParentheses("()()()()()(("));
console.log(validParentheses("))(())"));
I am trying to solve a task with regex. Given a function with string parameter .The string contains (){}<>[] braces. I have to check if the string is syntactically true and I should also take a count of braces nesting.
This is my version (incomplete)`
const checkBraces = (str) => {
const newStr = str.replace(/[^(){}<>[\]]+/gi, '');
let answer = newStr.match(/\(+\)+|\<+\>+|\{+\}+|\[+\]+/g);
console.log(answer);
}
and this is the minimal count of tests for the function `
checkBraces("---(++++)----") == 0
checkBraces("") == 0
checkBraces("before ( middle []) after ") == 0
checkBraces(") (") == 1
checkBraces("} {") == 1
checkBraces("<( >)") == 1
checkBraces("( [ <> () ] <> )") == 0
checkBraces(" ( [)") == 1
If there is en error so the function should return 1 , else 0 .
In my function I first tried to replace all non-braces so I have a clear string. Now I can't solve this problem .
You can solve this by iterating through the string and keeping a stack of opening braces. Each time you find a closing brace, pop from the stack. The closing brace should match the thing you popped or they're not balanced. At the end the stack should be empty:
let braces = { // lookup to match closing with opening
'(':')',
'{':'}',
'<':'>',
'[':']'
}
let closing = new Set([')', '}', '>', ']']) // quick lookup of brackets
let opening = new Set(['(', '{', '<', '['])
function checkBraces(str) {
/* returns true if balanced, false if unbalanced */
let stack = []
for (l of str){
if (closing.has(l)){ // found a closing bracket
if (l !== braces[stack.pop()]) return false // does it match the last opening?
} else if(opening.has(l)) stack.push(l) // found an opening push to the stack
}
return stack.length === 0
}
console.log(checkBraces("before ( middle []) after "))
console.log(checkBraces("<( >)"))
console.log(checkBraces("( [ <> () ] <> )"))
console.log(checkBraces(" ( [)"))
console.log(checkBraces(" )"))
console.log(checkBraces(" <"))
Not using any inbuilt function except push and pop.
function check(s) {
var arr = [];
for (let i = 0; i < s.length; i++) {
if (s[i] === '{' || s[i] === '[' || s[i] === '(' || s[i] === '<' ) {
arr.push(s[i]);
} else if (s[i] === '}' || s[i] === ']' || s[i] === ')' || s[i] === '>' ) {
if ( arr[arr.length-1] === s[i] ) {
arr.pop();
} else {
return 'not balanced';
}
}
}
if(arr.length) return 'not balanced';
return 'balanced';
}
console.log(check('{781234}[3,4,5,6 ]< >( sdfhniusdf )'));
console.log(check('asssssssssss {}');
console.log(check(' as< habsdj');
There are multiple solutions to how to check if parentheses are balanced, but I haven't found a single one that would be checking both for balanced quotes and parentheses.
I have been unsuccessfully trying to adapt this solution (codereview - balanced parentheses) to be able to check if the quotes and parentheses are balanced.
For example this should be unbalanced ("back-to-school)"
Original code:
function parenthesesAreBalanced(string) {
var parentheses = "[]{}()",
stack = [],
i, character, bracePosition;
for(i = 0; character = string[i]; i++) {
bracePosition = parentheses.indexOf(character);
if(bracePosition === -1) {
continue;
}
if(bracePosition % 2 === 0) {
stack.push(bracePosition + 1); // push next expected brace position
} else {
if(stack.length === 0 || stack.pop() !== bracePosition) {
return false;
}
}
}
return stack.length === 0;
}
My code - mostly similar - but added an unbalanced quotes check.
function areQuotesAndParenthesesBalanced(s: string): boolean {
const parens = '[]{}()',
parensStack = [];
let index, char, numOfQuotes = 0;
for (index = 0; char = s[index++];){
const bracePosition = parens.indexOf(char);
let braceType;
if (bracePosition === -1 && char !== '"')
continue;
braceType = bracePosition % 2 ? 'closed' : 'open';
//check for double quotes mixed with parentheses
if(char === '"'){
const lastInStack = parensStack[parensStack.length - 1];
numOfQuotes++;
if(lastInStack === '"'){
numOfQuotes--;
parensStack.pop();
}else if(numOfQuotes > 0 && lastInStack !== '"'){
return false;
}else{
parensStack.push('"');
}
}
if (braceType === 'closed') {
if (!parensStack.length || parens.indexOf(parensStack.pop()) != bracePosition - 1)
return false;
} else {
parensStack.push(char);
}
}
//If anything is left on the stack <- not balanced
return !parensStack.length;
}
It is quite tricky for me to determine what's the best approach. With parentheses, you always know when one is open or closed, with quotes, not so much.
function tokensAreBalanced(string) {
var asymmetricTokens = "[]{}()",
symmetricTokens = '"',
stack = [],
i, character, tokenPosition;
for(i = 0; character = string[i]; i++) {
tokenPosition = asymmetricTokens.indexOf(character);
if(tokenPosition >= 0) {
if(tokenPosition % 2 === 0) {
stack.push(asymmetricTokens[tokenPosition + 1]); // push next expected token
} else if(stack.length === 0 || stack.pop() !== character) {
return false;
}
} else {
if(symmetricTokens.includes(character)) {
if(stack.length > 0 && stack[stack.length - 1] === character) {
stack.pop();
} else {
stack.push(character);
}
}
}
}
return stack.length === 0;
}
console.log('("back-to-school)"', tokensAreBalanced('("back-to-school)"'));
console.log('("back-to-school)', tokensAreBalanced('("back-to-school)'));
console.log('("back-to-school")', tokensAreBalanced('("back-to-school")'));
console.log('(ele AND car) OR ("ele car)")', tokensAreBalanced('(ele AND car) OR ("ele car)")'));
This performs a check for push() or pop() of " in 2 ways.
If stack is empty or last character in stack does not equal ", then insert this " into stack.
If stack is not empty and last character in stack is equal to ", then pop() the " in stack itself. This is done because I do a form of greedy matching here since a " for already stack " means expression inside "..." was evaluated. So, we are safe to match these 2 " and proceed with the next.
Works well, but let me know if it fails for any case.
function areQuotesAndParenthesesBalanced(s){
var pairs = {
'}':'{',
']':'[',
')':'(',
};
var stack = [];
for(var i = 0;i < s.length;++i){
switch(s.charAt(i)){
case '[': case '{':case '(':
stack.push(s.charAt(i));
break;
case ']': case '}':case ')':
if(isStackEmpty(stack) || peek(stack) !== pairs[s.charAt(i)]) return false;
stack.pop();
break;
case '"':
if(isStackEmpty(stack) || peek(stack) !== s.charAt(i)){
stack.push(s.charAt(i));
}else{
stack.pop();
}
}
}
return isStackEmpty(stack);
}
function isStackEmpty(s){
return s.length === 0;
}
function peek(s){
return s[s.length-1];
}
var tests = {
'("back-to-school")':true,
'"(back-to-school)"':true,
'("back-to-school)"':false,
'("back-to-school)':false,
'"["["["[]"]"]"]"':true,
'"["]""':false,
'"[]"""':true,
'""""':true,
'""':true,
'"':false,
'""[("")]""':true,
'""[("")]':true,
'"["["["[]"]"[""]]"]':false,
'"[]"[({})]""':true,
'"[{}"]':false
};
for(var each_test in tests){
var res = areQuotesAndParenthesesBalanced(each_test);
console.log(each_test + " --> " + (res === tests[each_test] ? "ok" : "not ok") + " , expected : " + tests[each_test]);
}
OUTPUT
("back-to-school") --> ok , expected : true
"(back-to-school)" --> ok , expected : true
("back-to-school)" --> ok , expected : false
("back-to-school) --> ok , expected : false
"["["["[]"]"]"]" --> ok , expected : true
"["]"" --> ok , expected : false
"[]""" --> ok , expected : true
"""" --> ok , expected : true
"" --> ok , expected : true
" --> ok , expected : false
""[("")]"" --> ok , expected : true
""[("")] --> ok , expected : true
"["["["[]"]"[""]]"] --> ok , expected : false
"[]"[({})]"" --> ok , expected : true
"[{}"] --> ok , expected : false
You could try putting an ordered tuple on the stack and checking based off of that.
[(,"],
[",)],
[(,"],
[",)]
== ("")("") example of a balanced stack.
[",(],
[",(],
[),"],
[),"]
== "("()")" another balanced stack
[(,"],
[),"]
== (")" trivial unbalanced stack
[(,)] <- trivial item, can ignore in implementation
[","] <- trivial item, can ignore in implementation
[",(],
[),(],
[),"]
== "()()" balanced stack
I'm too tired to actually implement this, but hopefully it gave you some ideas and illustrative examples, I'll revisit it after I get some sleep.
I would suggest simplifying the input by removing all quoted substrings. Quotes behave differently because parentheses inside quotes are not treated specially, but as normal characters. By first removing each quoted part, this "problem" is resolved from the start. Also irrelevant characters (that are not parentheses nor quotes) can be removed as well. That way we are only left with a string that has parentheses and possibly a single occurrence of a quote. The latter would immediately make the input invalid.
Here is code similar to #nice_dev's answer, with that manipulation implemented and with pairs inversed; it also deals with apostrophe as alternative delimiter:
function areQuotesAndParenthesesBalanced(s) {
const pairs = {
'{':'}',
'[':']',
'(':')',
};
const stack = [""]; // Dummy
for (const token of s.replace(/"[^"]*"|'[^']*'|[^"'{}()[\]]+/g, "")) {
if (token in pairs) {
stack.push(pairs[token]);
} else {
if (stack.at(-1) !== token) return false;
stack.pop();
}
}
return stack.length == 1; // Empty
}
// Same tests as in the answer of #nice_dev:
const tests = {
'("back-to-school")': true,
'"(back-to-school)"': true,
'("back-to-school)"': false,
'("back-to-school)': false,
'"["["["[]"]"]"]"': true,
'"["]""': false,
'"[]"""': true,
'""""': true,
'""': true,
'"': false,
'""[("")]""': true,
'""[("")]': true,
'"["["["[]"]"[""]]"]': false,
'"[]"[({})]""': true,
'"[{}"]': false
};
for (const each_test in tests){
const res = areQuotesAndParenthesesBalanced(each_test);
console.log(`${each_test} --> ${res === tests[each_test] ? "ok" : "not ok"}, expected: ${tests[each_test]}`);
}
A simple approaching way might be like this for only first braces:
function Search(str) {
const onlyBrackets = str.replace(/[a-zA-Z]/g, "");
const left = onlyBrackets.replace(/[)]/g, "");
const right = onlyBrackets.replace(/[(]/g, "");
str = left.length === right.length ? 1 : 0
return str
}
console.log(Search("(coder)(byte))")) // 0
console.log(Search("(c(oder))b(yte)")) // 1
function isParenthesisBalanced(_str) {
var parenMap = {'{':'}', '[':']', '(':')'};
var parenStack =[];
for(var i=0;i<_str.length; i++) {
if(_str[i] in parenMap) {
parenStack.push(_str[i]);
} else if(Object.values(parenMap).indexOf(_str[i]) != -1) {
if(parenMap[parenStack.pop()] != _str[i]) return false;
}
}
return true;
}