4
\$\begingroup\$

I wrote simple function for checking if string is balanced (with brackets ([{}])). It takes a string argument and need to return boolean value (true if string is balanced and false if not).

const isBalanced = (string) => {
  const arr = string.split('');
  let open = [];
  
  const openBrackets = {
    '(': true,
    '[': true,
    '{': true,
  };
  
  const closedBrackets = {
    ')': '(',
    ']': '[',
    '}': '{',
  };
  
  for (let i = 0, length = arr.length; i < length; i++) {
    if (openBrackets[arr[i]]) {
      open.push(arr[i]);
    } else if (closedBrackets[arr[i]] && open.pop() !== closedBrackets[arr[i]]) {
      return false;
    }
  }
  
  return !open.length;
}

console.log(isBalanced('') === true);
console.log(isBalanced('()') === true);
console.log(isBalanced(')()') === false);
console.log(isBalanced(')(') === false);
console.log(isBalanced('()(()()()())') === true);
console.log(isBalanced('[](){}') === true);
console.log(isBalanced('[({})]') === true);
console.log(isBalanced('[(]{)}') === false);
console.log(isBalanced('const sth = () => { animal.sleep();') === false);
console.log(isBalanced('const sth = { bark: animal() }') === true);

Do you think that i could improve it somehow? Can you see any mistakes?

\$\endgroup\$
2
\$\begingroup\$

openBrackets can be a Set

openBrackets is only used to test if a character is an opening bracket. So, it can be declared like this:

const openBrackets = new Set(['(', '[', '{']);

and the test will become:

if (openBrackets.has(arr[i])) { ... }

Use for...of to iterate

The for ... of syntax avoids having to work with indices, as in this case you don't need the i variable at all (except to dereference the correct character from arr). Furthermore, you can iterate the string directly, without having to .split('') it first:

for (let c of string) {
  if (openBrackets.has(c)) {
    open.push(c);
  } else if (closedBrackets[c] && open.pop() !== closedBrackets[c]) {
    return false;
  }
}

Rename closedBrackets

Reading the name of this variable I would expect some sort of collection of brackets, not a mapping/correspondence between closing and opening brackets. Therefore, I would give it a different name. Something like bracketCorrespondence might work, although there might be some better names for it out there.

So, here's a version of the code with these suggestions applied:

const isBalanced = (string) => {
  let open = [];
  
  const openBrackets = new Set(['(', '[', '{']);

  const bracketCorrespondence = {
    ')': '(',
    ']': '[',
    '}': '{',
  };
  
  for (let c of string) {
    if (openBrackets.has(c)) {
      open.push(c);
    } else if (bracketCorrespondence[c] && open.pop() !== bracketCorrespondence[c]) {
      return false;
    }
  }
  
  return !open.length;
}

console.log(isBalanced('') === true);
console.log(isBalanced('()') === true);
console.log(isBalanced(')()') === false);
console.log(isBalanced(')(') === false);
console.log(isBalanced('()(()()()())') === true);
console.log(isBalanced('[](){}') === true);
console.log(isBalanced('[({})]') === true);
console.log(isBalanced('[(]{)}') === false);
console.log(isBalanced('const sth = () => { animal.sleep();') === false);
console.log(isBalanced('const sth = { bark: animal() }') === true);

\$\endgroup\$
0
\$\begingroup\$

The other answer pretty much sums it up so no need to repeat.

An alternative version for this would be to use recursion. A little more compact than the last, and the stack gets sent in as an argument so you don't deal with stateful vars outside the cycle.

The patterns had to be moved outside to avoid reconstructing them on each call. But they're not modified in any way so they're essentially immutable values that you can rely on.

const open = new Set(['{', '[', '('])
const close = new Set(['}', ']', ')'])
const pairs = { '{':'}', '[':']', '(': ')' }

function isBalanced(str, stack = []){
  const next = str[0]

  if(str === '') {
    // false if ran out of string and something is on the stack
    return !stack.length
  } else if(close.has(next) && pairs[stack.pop()] !== next) {
    // false if the closing doesn't match the top of the stack
    return false
  } else if(open.has(next)) {
    // it's an opener, push into the stack
    stack.push(next)
  } else {
    // Do nothing, it's not a bracket of any sort
  }

  // Do the same with the next sequence
  return isBalanced(str.slice(1), stack)
}

console.log(isBalanced('') === true);
console.log(isBalanced('()') === true);
console.log(isBalanced(')()') === false);
console.log(isBalanced(')(') === false);
console.log(isBalanced('()(()()()())') === true);
console.log(isBalanced('[](){}') === true);
console.log(isBalanced('[({})]') === true);
console.log(isBalanced('[(]{)}') === false);
console.log(isBalanced('const sth = () => { animal.sleep();') === false);
console.log(isBalanced('const sth = { bark: animal() }') === true);

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.