# JavaScript function that flips brackets direction

I have these strings:

)hello(
this has ]some text[
flip }any{ brackets
even with )))]multiple[((( brackets


As you can see, the brackets are all in the wrong direction.

I have a function called flipBracketsDirection() for each string. Here is an example of input and output of the above strings:

flipBracketsDirection(')hello(');
// should return:  (hello)

flipBracketsDirection('this has ]some text[');
// should return:  this has [some text]

flipBracketsDirection('flip }any{ brackets');
// should return:  flip {any} brackets

flipBracketsDirection('even with )))]multiple[((( brackets');
// should return:  even with ((([multiple]))) brackets


Note: The direction is flipped at all times. So this is fine too:

flipBracketsDirection('flip (it) anyway');
// should return:  flip )it( anyway


Here is my solution.

function flipBracketsDirection(str: string) {
return str
// flip () brackets
.replace(/$$/g, 'tempBracket').replace(/$$/g, '(').replace(/tempBracket/g, ')')

// flip [] brackets
.replace(/$/g, 'tempBracket').replace(/$/g, '[').replace(/tempBracket/g, ']')

// flip {} brackets
.replace(/\{/g, 'tempBracket').replace(/\}/g, '{').replace(/tempBracket/g, '}')
;
}


Is this the best way to create this function?

Your function seems to work fine, but you can condense it a bit by search for all bracket types in one regex replace with the following pattern:

/[\{\}]/g


and then use the replace function that takes a replace function as argument:

const brs = "()({}{[][";
function flipBracketsDirection(str) {
return str.replace(/[\{\}]/g, br => brs[brs.indexOf(br) + 1]);
}


brs holds all the replaceable brackets with the opening brackets twice, so brs[brs.indexOf(')') + 1] finds '(' as the next char in brs;

You could also let brs be an object like:

const brs =
{
"(": ')',
")": '(',
"{": '}',
"}": '{',
"[": ']',
"]": '[',
};


and then cquery it as:

function flipBracketsDirection(str) {
return str.replace(/[\{\}]/g, br => brs[br]);
}


Your version actually query the string nine times, where the above only iterates it once.

As an alternative to the 'sophisticated' brs dictionary-solution, you could just create a function with a switch statement:

function swapBracket(br) {
switch (br) {
case '(': return ')';
case ')': return '(';
case '{': return '}';
case '}': return '{';
case '[': return ']';
case ']': return '[';
}
}


And call that instead, this way:

function flipBracketsDirection(str: string) {
return str.replace(/[\{\}]/g, swapBracket);
}

flipBracketsDirection('}hello{');
// will return {hello}


Your tests cases could also be simplified, so it is easier to maintain - for instance like:

  let strs = [
')hello(',
'this has ]some text[',
'flip }any{ brackets',
'even with )))]multiple[((( brackets'
];

for (let s of strs) {
let t = flipBracketsDirection(s);
console.log(t);
console.log(flipBracketsDirection(t));
console.log("");
}


Both the above suggestions should conform to the DRY principle.

• What's you're opinion on brs = '(){}[]', brs[brs.indexOf(br) ^ 1]? – Peilonrayz Jun 17 at 13:16
• Too clever for me @Peilonrayz. Sure, it works, but coming back to that in a month..? – Gerrit0 Jun 17 at 13:20
• @Gerrit0 I've never heard anyone say xor is 'too clever' before. – Peilonrayz Jun 17 at 13:23
• @Elron bitwise XOR – Sᴀᴍ Onᴇᴌᴀ Jun 17 at 23:22
• @Peilonrayz: For what it's worth, I find your version much clearer, and less error-prone. The next dev might not notice that the brackets need to be written 3 times otherwise, and would simply add brs = "<>()({}{[][". – Eric Duminil Jun 18 at 18:28

It may seem unlikely but it is not impossible for an input string to contain tempBracket so a solution that doesn't involve adding and replacing that string would be ideal.

function flipBracketsDirection(str) {
return str
// flip () brackets
.replace(/$$/g, 'tempBracket').replace(/$$/g, '(').replace(/tempBracket/g, ')')

// flip [] brackets
.replace(/$/g, 'tempBracket').replace(/$/g, '[').replace(/tempBracket/g, ']')

// flip {} brackets
.replace(/\{/g, 'tempBracket').replace(/\}/g, '{').replace(/tempBracket/g, '}')
;
}
console.log(flipBracketsDirection(')will tempBracket get replaced?('))

The answer by @Henrik already suggests using a single regular expression to replace any characters in the group of brackets.

A mapping of characters seems an ideal solution in terms of performance. The mapping can be frozen using Object.freeze() to avoid alteration.

const BRACKET_MAPPING = Object.freeze({
"(": ')',
")": '(',
"{": '}',
"}": '{',
"[": ']',
"]": '[',
});
const mapBracket = (bracket: string) => BRACKET_MAPPING[bracket];
const flipBracketsDirection = (str: string) => str.replace(/[\{\}]/g, mapBracket);

• This looks clear and concise. Would it be possible to generate the regex from the keys of BRACKET_MAPPING? – Eric Duminil Jun 18 at 18:27
• yes- likely with the Regexp constructor – Sᴀᴍ Onᴇᴌᴀ Jun 18 at 18:32

This is an answer that I posted to the original question on StackOverflow. It does away with regular expressions entirely, and uses a similar case-statement to the one suggested in @Henrik's answer

The original code performs 6 regular expression substitutions (which require 1 pass each), and fails on strings that contain the text tempBracket (as noted by @Kaiido in comments to the StackOverflow question).

This should be quicker because it makes a single pass, and requires no regular expressions at all. If all characters are ASCII, the flip function could be rewritten to use a look-up table, which would make it branch-free and potentially even faster.

function flipBracketsDirection(str) {
function flip(c) {
switch (c) {
case '(': return ')';
case ')': return '(';
case '[': return ']';
case ']': return '[';
case '{': return '}';
case '}': return '{';
default: return c;
}
}
return Array.from(str).map(c => flip(c)).join('');
}

// testcases
let test = (x) => console.log(flipBracketsDirection(x));
test('flip (it) anyway');
test(')hello(');
test('this has ]some text[');
test('flip }any{ brackets');
test('even with )))]multiple[((( brackets');

• Can you also post this in a new post, with the tag rags-to-riches? I have some thoughts 😁. – Sᴀᴍ Onᴇᴌᴀ Jun 18 at 13:56
• @SᴀᴍOnᴇᴌᴀ you hereby have my permission to post my answer as a question to then answer it with your answer :-). I'll be happy to look at it (answer the comment so that I know to look). – tucuxi Jun 18 at 14:20
• I wouldn't really be able to (morally) post your code in a question because I wouldn't be able to answer "yes" to all the questions in the What topics can I ask about here? page... (hint/protip: we can both gain more rep that way) – Sᴀᴍ Onᴇᴌᴀ Jun 18 at 14:28
• How bad is the performance hit of converting from string to array and back again, compared to regular expression replacement? I may be mistaken, but it seems like this would be more expensive than a simple regex – Charlie Harding Jun 19 at 0:40
• It seems like the regex wins out by 5-10% (jsperf.com/reverse-brackets) – Charlie Harding Jun 19 at 0:59

One for the fun of it

const flipBrackets = BracketFlipper();
[ ')hello(',
'this has ]some text[',
'flip }any{ brackets',
'even with )))]multiple[((( brackets',
'flip (it) anyway',
'>Pointy stuff<',
'/slashed\\'].forEach(s => console.log(flipBrackets(s)));;

function BracketFlipper() {
const bracks = "(),{},[],<>,\\\/".split(",");
const brackets = [
...bracks,
...bracks.reverse()
.map(v => [...v].reverse().join("")) ]
.reduce( (a, v) => ({...a, [v[0]]: v[1] }), {} );
const re = new RegExp( [${Object.keys(brackets).map(v => \\${v}).join("")}], "g" );
return str => str.replace(re, a => brackets[a]);
}
.as-console-wrapper { top: 0; max-height: 100% !important; }