The Function to check mathematical expression not working.
I debugged this on chrome, and i saw that when it gets to the first pop (stack.pop()!== chars[i]), it returns false, but it shouldn't.
var smarter_validate = function(str) {
var chars = str.split('');
var stack = [];
var lookup = {
'(': ')',
'[': ']',
'{': '}',
'<': '>'
};
var left = Object.keys(lookup);
var right = Object.keys(lookup).map(function(key) {
return lookup[key]
});
for (var i = 0; i < chars.length; i++) {
if (left.indexOf(chars[i]) !== (-1)) {
stack.push(chars[i]);
} else if (right.indexOf(chars[i]) !== (-1)) {
if ((stack.length === 0) || (stack.pop() !== chars[i])) {
return false;
}
}
}
return (stack.length === 0);
};
console.log("SMART VALIDATE" + smarter_validate('(3+4[*2{6+8}])'));
You actually have to compare the popped value's corresponding closing character with chars[i], not the popped value itself.
So you need to do
if (stack.length === 0 || lookup[stack.pop()] !== chars[i]) {
Now, when you { from the stack, you will look for the corresponding closing character from the lookup and compare it with the current closing character.
Alternatively you can simply push the expected closing character in the stack so that you don't have do the lookup during the comparison, like this
stack.push(lookup[chars[i]]);
Related
I am a newbie who is trying hard to have a grip on javascript. please help me to consolidate my fundamentals.
input will be a string of letters.
following are the requirements.
function should return true if following conditions satisfy:
letters are in alphabetical order. (case insensitive)
only one letter is passed as input. example :
isAlphabet ('abc') === true
isAlphabet ('aBc') === true
isAlphabet ('a') === true
isAlphabet ('mnoprqst') === false
isAlphabet ('') === false
isAlphabet ('tt') === false
function isAlphabet(letters) {
const string = letters.toLowerCase();
for (let i = 0; i < string.length; i++) {
const diff = string.charCodeAt(i + 1) - string.charCodeAt(i);
if (diff === 1) {
continue;
} else if (string === '') {
return false;
} else if (string.length === 1) {
return true;
} else {
return false;
}
}
return true;
}
It's generally a better practice to start your function off with dealing with the edge-cases rather than putting them somewhere in the middle. That way, the function returns as soon as it can - and it's a lot easier to read than a waterfall of if..else statements.
function isAlphabet(letters) {
if ("" == letters) {
return false;
}
if (1 == letters.length) {
return true;
}
const string = letters.toLowerCase();
// carry on with your loop here.
}
You've got the right idea, but it can be simplified to just fail on a particular error condition, i.e when a smaller character follows a larger one:
function isAlphabet(letters) {
const string = letters.toLowerCase();
let lastChar;
for (let i = 0; i < string.length; i++) {
// Grab a character
let thisChar = string.charCodeAt(i);
// Check for the failure case, when a lower character follows a higher one
if (i && (thisChar < lastChar)) {
return false;
}
// Store this character to check the next one
lastChar = thisChar;
}
// If it got this far then input is valid
return true;
}
console.log(isAlphabet("abc"));
console.log(isAlphabet("aBc"));
console.log(isAlphabet("acb"));
You can use the simple way to achieve the same as below
function isAlphabet(inputString)
{
var sortedString = inputString.toLowerCase().split("").sort().join("");
return sortedString == inputString.toLowerCase();
}
console.log("abc = " + isAlphabet("abc"));
console.log("aBc = " + isAlphabet("aBc"));
console.log("acb = " + isAlphabet("acb"));
console.log("mnoprqst = " + isAlphabet("mnoprqst"));
Note: Mark the answer is resolves your problem.
I'm working on a coding challenge that involves valid parenthesis and I'm trying to troubleshoot why it's not giving me the intended response.
function isValid(str) {
if (str.length === 0)
return true
let matchingOpeningBracket, char
let stack = []
let openingBrackets = ['[', '<', '(']
let closingBrackets = [']', '>', ')']
for (let i = 0; i < str.length; i++) {
char = str[i]
if (closingBrackets.indexOf(char) > -1) {
matchingOpeningBracket = openingBrackets[closingBrackets.indexOf(char)]
if (stack.length == 0 || (stack.pop() != matchingOpeningBracket)) {
return false
}
} else {
stack.push(char)
}
}
return (stack.length == 0)
};
When I try to check console.log(isValid('---(++++)---')) and console.log(isValid('before(middle[])after')) it gives me a false value but it should be passing as true according to the challenge key. I'm not sure what step I'm missing. The other tests examples are logging as intended as seen here except for the first 2.
//true // returns false
// let example1 = ("before(middle[])after")
//true //returns false
// let example1 = ("---(++++)---")
//true
// let example1 = ("")
//false
// let example1 = (")(")
//false
// let example1 = ("<(>)")
//true
// let example1 = ("([<>()])")
//false
// let example1 = ("([)")
You are pushing everything that is not a closing bracket onto the stack when you should ignore chars that are neither opening or closing brackets. Change your else condition to this:
else if (openingBrackets.indexOf(char) > -1) {
I definitely would not want to complicate things so I am giving a simple solution, so if you see it fit you can use it :)
function isValid(str) {
if (str.length === 0)
return true
let char;
let stack = [];
let openingBrackets = ['[', '<', '(']
let closingBrackets = [']', '>', ')']
matchesOpen = (val) => (val === '[' || val === '<' || val === '(')
matchesclose = (val) => (val === ']' || val === '>' || val === ')')
getOpeningBracket = (val) => {
switch (val) {
case ']':
return '[';
case ')':
return '(';
case '>':
return '<';
default:
return '';
}
}
for (let i = 0; i < str.length; i++) {
char = str[i]
if (matchesOpen(char)) {
stack.push(char);
} else if (matchesclose(char)) {
if (stack.pop() !== getOpeningBracket(char)) return false;
}
}
return (stack.length == 0)
};
console.log(isValid(('---(++++)---')))
console.log(isValid('before(middle[])after'))
console.log(isValid("---(++++)---"))
You’re pushing onto the stack every char that is not an opening parenthesis. You only want to push opening parentheses onto the stack.
Notice that you get a false result whenever there are non-parenthesis characters in the string.
Need to find open and closed bracket, if the sequence of opening and closing brackets is violated, then return false.
But if don't revert right array to compare with left array, i don't make check brackets here {[(3+1)+2]+}. And if reverse like now, then i fail to check here [1+1]+(2*2)-{3/3}
function brackets(expression){
let leftArr=[];
let rightArr = [];
for(let i=0; i<expression.length; i++){
if(expression[i] === '(' || expression[i] === '[' || expression[i] === "{"){
leftArr.push(expression[i]);
}
if(expression[i] === ')'){
rightArr.push("(");
}else if(expression[i] === '}'){
rightArr.push("{");
} else if(expression[i] === ']'){
rightArr.push("[");
}
}
rightArr.reverse();
if(leftArr.length<rightArr.length || leftArr.length>rightArr.length){
return false;
}
for(let k=0; k<leftArr.length; k++) {
if(leftArr[k] != rightArr[k]){
return false;
}
}
return true;
}
console.log(brackets('(3+{1-1)}')); // false
console.log(brackets('{[(3+1)+2]+}')); //true
console.log(brackets('[1+1]+(2*2)-{3/3}')); //true
console.log(brackets('(({[(((1)-2)+3)-3]/3}-3)')); //false
In the shortest possible, with comments for lines that are probably confusing for you.
function check(expr){
const holder = []
const openBrackets = ['(','{','[']
const closedBrackets = [')','}',']']
for (let letter of expr) { // loop trought all letters of expr
if(openBrackets.includes(letter)){ // if its oppening bracket
holder.push(letter)
}else if(closedBrackets.includes(letter)){ // if its closing
const openPair = openBrackets[closedBrackets.indexOf(letter)] // find its pair
if(holder[holder.length - 1] === openPair){ // check if that pair is the last element in the array
holder.splice(-1,1) // if so, remove it
}else{ // if its not
holder.push(letter)
break // exit loop
}
}
}
return (holder.length === 0) // return true if length is 0, otherwise false
}
check('[[{asd}]]') /// true
Right now you are getting every single open bracket into one array, then pushing an open bracket for every closing one into another array, then comparing them. That's a bit wasteful.
Instead, you can maintain a stack. Push an open tag onto the stack and if you find a close bracket - pop from the stack
if there is no match or nothing on the stack when you pop, terminate with a failure
if you finish with a stack size of zero, then you are successful
function brackets(expression) {
let stack = [];
let current;
const matchLookup = {
"(": ")",
"[": "]",
"{": "}",
};
for (let i = 0; i < expression.length; i++) {
current = expression[i]; //easier than writing it over and over
if (current === '(' || current === '[' || current === "{") {
stack.push(current);
} else if (current === ')' || current === ']' || current === "}") {
const lastBracket = stack.pop();
if (matchLookup[lastBracket] !== current) { //if the stack is empty, .pop() returns undefined, so this expression is still correct
return false; //terminate immediately - no need to continue scanning the string
}
}
}
return stack.length === 0; //any elements mean brackets left open
}
console.log(brackets('(3+{1-1)}')); // false
console.log(brackets('{[(3+1)+2]+}')); //true
console.log(brackets('[1+1]+(2*2)-{3/3}')); //true
console.log(brackets('(({[(((1)-2)+3)-3]/3}-3)')); //false
I have used an object to lookup the values but it need not be one. An alternative is to use two arrays that you have to keep in sync
opening = ["(", "[", "{"]
closing = [")", "]", "}"]
On the other hand, if you have those, you can shorten your if checks to if (open.includes(current)) and if (closing.includes(current)).
This can be an easier solution:
const checkBrackets = (expression) => {
const stack = [];
const bracketLookup = {
'{': '}',
'(': ')',
'[': ']',
};
for (const key of expression) {
if(Object.keys(bracketLookup).includes(key)) { // matches open brackets
stack.push(key);
} else if(Object.values(bracketLookup).includes(key)) { //matches closed brackets
const lastBracket = stack.pop();
if(bracketLookup[lastBracket] !== key) {
return false;
}
}
}
return stack.length === 0;
}
Results:
checkBrackets('a(fg(a)}'); // false
checkBrackets('[1+1)+(2*2]-{3/3}'); // false
checkBrackets('a(d-h)+y{hh}||[hh-a-]'); // true
You can use stack with switch statement with a single for loop for efficient time and space complexity
function checkParantesis(str) {
const stack = [];
for (let s of str) {
if (s == '(' || s == '[' || s == '{') {
stack.push(s);
continue;
}
if (stack.length === 0) {
return false
}
switch (s) {
case ')':
stack.pop();
if (s == '{' || s == '[') {
return false
}
break;
case '}':
stack.pop();
if (s == '(' || s == '[') {
return false
}
break;
case ']':
stack.pop();
if (s == '{' || s == '(') {
return false
}
break;
}
}
return stack.length ? false : true
}
const output = checkParantesis('{{}}'));
console.log(output)
You can use the function String.prototype.replace to gather the brackets and use a kind of stack to compare each char. The stack is useful in order to know what was the last pushed bracket.
let check = (e) => {
let brackets = [],
stack = [],
map = {'}': '{', ']': '[', ')': '('};
e.replace(/[\[\]\{\}\(\)]/g, (m) => m && brackets.push(m));
for (let i = 0, {length} = brackets; i < length; i++) {
if (['}', ']', ')'].includes(brackets[i])) {
if (stack.pop() !== map[brackets[i]]) return false;
} else stack.push(brackets[i]);
}
return !stack.length;
};
console.log(check('(3+{1-1)}')); // false
console.log(check('{[(3+1)+2]+}')); //true
console.log(check('[1+1]+(2*2)-{3/3}')); //true
console.log(check('(({[(((1)-2)+3)-3]/3}-3)')); //false
I hope this will solve your problem...
function brackets(expression) {
let leftArr=[];
for(let i=0; i<expression.length; i++) {
if(expression[i] === '(' || expression[i] === '[' || expression[i] === "{") {
leftArr.push(expression[i]);
}
let leftArrLength = leftArr.length;
if(expression[i] === ')' && leftArr[leftArrLength - 1] === '('){
leftArr.pop();
}else if(expression[i] === '}' && leftArr[leftArrLength - 1] === '{') {
leftArr.pop();
} else if(expression[i] === ']' && leftArr[leftArrLength - 1] === '[') {
leftArr.pop();
}
else if(expression[i] === ')' || expression[i] === '}' || expression[i] === ']'){
return false;
}
}
return leftArr.length === 0;
}
console.log(brackets('(3+{1-1)}')); // false
console.log(brackets('{[(3+1)+2]+}')); //true
console.log(brackets('[1+1]+(2*2)-{3/3}')); //true
console.log(brackets('(({[(((1)-2)+3)-3]/3}-3)')); //false
console.log(brackets('(((([[[[{{{3}}}]]]]))))')); //false
My approach will be a little different.
These bracket pairs lie in the ASCII pair right after there first occurence.
Means '(' is placed (at 41) after ')' (at 40).
So, if there is a string input {[()]} and are in order.
We can divide the string by length/2 and check for ASCII value + 1
I think this the best solution.
const checkBracketSequenceBalance = (exp) => {
const pairs = {
'(': ')',
'[': ']',
'{': '}'
},
open = []
for (let i = 0; i < exp.length; i++)
if (pairs[exp[i]])
open.push(exp[i])
else if (exp[i] === pairs[open[open.length - 1]])
open.pop()
return !open.length
}
var input = "[({([])})]";
console.log(checkPairs(input));
function checkPairs(input=null) {
var arr = input.split("");
var result = false;
var tmpArr = [];
if ((arr.length % 2) == 0) {
arr.forEach(element => {
if (tmpArr[element] == null) {
tmpArr[element] = 1;
} else {
tmpArr[element] += 1;
}
});
if (tmpArr['['] == tmpArr[']'] && tmpArr['('] == tmpArr[')'] && tmpArr['{'] == tmpArr['}']) {
result = true;
}
}
return result;
}
input
books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters
the idea is to split the string by dots but ignore those inside square brackets
so after splitting there should be an array
[
'books',
'copies',
'[read_by.[p_id="65784"].page=5468]',
'text',
'[paragraph="20"]',
'letters'
]
I already looked at this answer but it doesn't work with nested square brackets, which is what i need. Also I'm using javascript, so negative lookbehinds are not supported.
Help is much appreciated.
Edit 1: expand example
It isn't possible to do it with a regex in Javascript that isn't able to match nested structures. You need to use the good old method: a stack.
var text = 'books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters';
var item = '', result = [], stack = 0;
for (var i=0; i < text.length; i++) {
if ( text[i] == '.' && stack == 0 ) {
result.push(item);
item = '';
continue;
} else if ( text[i] == '[' ) {
stack++;
} else if ( text[i] == ']' ) {
stack--;
}
item += text[i];
}
result.push(item);
console.log(result);
You need to write a parser for this since a JavaScript regex does not support regex recursion, nor balanced constructs.
The point in these functions is that they keep a stack (level, openBrackets) of opening delimiters (in your case, it is [) and then check the stack state: if the stack is not emppty, the found . is considered inside the brackets, and is thus just appended to the current match. Else, when the stack is empty, the . found is considered outside of brackets, and is thus used to split on (the current value is appended to the output array (result, ret)).
function splitByDotsOutsideBrackets(string){
var openBrackets = 0, ret = [], i = 0;
while (i < string.length){
if (string.charAt(i) == '[')
openBrackets++;
else if (string.charAt(i) == ']')
openBrackets--;
else if (string.charAt(i) == "." && openBrackets == 0){
ret.push(string.substr(0, i));
string = string.substr(i + 1);
i = -1;
}
i++;
}
if (string != "") ret.push(string);
return ret;
}
var res = splitByDotsOutsideBrackets('books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters');
console.log(res);
Or another variation:
function splitOnDotsOutsideNestedBrackets(str) {
var result = [], start = 0, level = 0;
for (var i = 0; i < str.length; ++i) {
switch (str[i]) {
case '[':
++level;
break;
case ']':
if (level > 0)
--level;
break;
case '.':
if (level)
break;
if (start < i)
result.push(str.substr(start, i - start));
start = i + 1;
break;
}
}
if (start < i)
result.push(str.substr(start, i - start));
return result;
}
var s = 'books.copies.[read_by.[p_id="65784"].page=5468].text.[paragraph="20"].letters';
console.log(splitOnDotsOutsideNestedBrackets(s))
Adapted from one of my previous answers.
If you make use of the Function.length property, you get the total amount of arguments that function expects.
However, according to the documentation (as well as actually trying it out), it does not include Default parameters in the count.
This number excludes the rest parameter and only includes parameters before the first one with a default value
- Function.length
Is it possible for me to somehow get a count (from outside the function) which includes Default parameters as well?
Maybe you can parse it yourself, something like:
function getNumArguments(func) {
var s = func.toString();
var index1 = s.indexOf('(');
var index2 = s.indexOf(')');
return s.substr(index1 + 1, index2 - index1 - 1).split(',').length;
}
console.log(getNumArguments(function(param1, param3 = 'test', ...param2) {})); //3
Copying my answer over to here from a duplicate question:
Well, it's a bit of a mess but I believe this should cover most edge cases.
It works by converting the function to a string and counting the commas, but ignoring commas that are in strings, in function calls, or in objects/arrays. I can't think of any scenarios where this won't return the proper amount, but I'm sure there is one, so this is in no way foolproof, but should work in most cases.
UPDATE: It's been pointed out to me that this won't work for cases such as getNumArgs(a => {}) or getNumArgs(function(a){}.bind(null)), so be aware of that if you try to use this.
function getNumArgs(func) {
var funcStr = func.toString();
var commaCount = 0;
var bracketCount = 0;
var lastParen = 0;
var inStrSingle = false;
var inStrDouble = false;
for (var i = 0; i < funcStr.length; i++) {
if (['(', '[', '{'].includes(funcStr[i]) && !inStrSingle && !inStrDouble) {
bracketCount++;
lastParen = i;
} else if ([')', ']', '}'].includes(funcStr[i]) && !inStrSingle && !inStrDouble) {
bracketCount--;
if (bracketCount < 1) {
break;
}
} else if (funcStr[i] === "'" && !inStrDouble && funcStr[i - 1] !== '\\') {
inStrSingle = !inStrSingle;
} else if (funcStr[i] === '"' && !inStrSingle && funcStr[i - 1] !== '\\') {
inStrDouble = !inStrDouble;
} else if (funcStr[i] === ',' && bracketCount === 1 && !inStrSingle && !inStrDouble) {
commaCount++;
}
}
// Handle no arguments (last opening parenthesis to the last closing one is empty)
if (commaCount === 0 && funcStr.substring(lastParen + 1, i).trim().length === 0) {
return 0;
}
return commaCount + 1;
}
Here are a few tests I tried it on: https://jsfiddle.net/ekzuvL0c/
Here is a function to retrieve the 'length' of a function (expression or object) or an arrow function expression (afe). It uses a regular expression to extract the arguments part from the stringified function/afe (the part between () or before =>) and a regular expression to cleanup default values that are strings. After the cleanups, it counts the comma's, depending on the brackets within the arguments string.
Note This will always be an approximation. There are edge cases that won't be covered. See the tests in this Stackblitz snippet
const determineFnLength = fnLenFactory();
console.log(`fnTest.length: ${determineFnLength(fnTest)}`);
function fnTest(a,
b,
c = 'with escaped \' quote and, comma',
d = "and double \" quotes, too!" ) { console.log(`test123`); }
function fnLenFactory() {
const fnExtractArgsRE = /(^[a-z_](?=(=>|=>{)))|((^\([^)].+\)|\(\))(?=(=>|{)))/g;
const valueParamsCleanupRE = /(?<=[`"'])([^\`,].+?)(?=[`"'])/g;
const countArgumentsByBrackets = params => {
let [commaCount, bracketCount, bOpen, bClose] = [0, 0, [...`([{`], [...`)]}`]];
[...params].forEach( chr => {
bracketCount += bOpen.includes(chr) ? 1 : bClose.includes(chr) ? -1 : 0;
commaCount += chr === ',' && bracketCount === 1 ? 1 : 0; } );
return commaCount + 1; };
const extractArgumentsPartFromFunction = fn => {
let fnStr = fn.toString().replace(RegExp(`\\s|function|${fn.name}`, `g`), ``);
fnStr = (fnStr.match(fnExtractArgsRE) || [fn])[0]
.replace(valueParamsCleanupRE, ``);
return !fnStr.startsWith(`(`) ? `(${fnStr})` : fnStr; };
return (func, forTest = false) => {
const params = extractArgumentsPartFromFunction(func);
const nParams = params === `()` ? 0 : countArgumentsByBrackets(params);
return forTest ? [params, nParams] : nParams;
};
}