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()
to handle each scenario. 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?
4 Answers 4
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.
-
3\$\begingroup\$ What's you're opinion on
brs = '(){}[]'
,brs[brs.indexOf(br) ^ 1]
? \$\endgroup\$2020年06月17日 13:16:38 +00:00Commented Jun 17, 2020 at 13:16 -
3\$\begingroup\$ Too clever for me @Peilonrayz. Sure, it works, but coming back to that in a month..? \$\endgroup\$Gerrit0– Gerrit02020年06月17日 13:20:51 +00:00Commented Jun 17, 2020 at 13:20
-
3\$\begingroup\$ @Gerrit0 I've never heard anyone say xor is 'too clever' before. \$\endgroup\$2020年06月17日 13:23:24 +00:00Commented Jun 17, 2020 at 13:23
-
1\$\begingroup\$ @Elron bitwise XOR \$\endgroup\$2020年06月17日 23:22:36 +00:00Commented Jun 17, 2020 at 23:22
-
1\$\begingroup\$ @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 = "<>()({}{[]["
. \$\endgroup\$Eric Duminil– Eric Duminil2020年06月18日 18:28:56 +00:00Commented Jun 18, 2020 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);
-
\$\begingroup\$ This looks clear and concise. Would it be possible to generate the regex from the keys of
BRACKET_MAPPING
? \$\endgroup\$Eric Duminil– Eric Duminil2020年06月18日 18:27:19 +00:00Commented Jun 18, 2020 at 18:27 -
\$\begingroup\$ yes- likely with the Regexp constructor \$\endgroup\$2020年06月18日 18:32:09 +00:00Commented Jun 18, 2020 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');
-
\$\begingroup\$ Can you also post this in a new post, with the tag rags-to-riches? I have some thoughts 😁. \$\endgroup\$2020年06月18日 13:56:09 +00:00Commented Jun 18, 2020 at 13:56
-
\$\begingroup\$ @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). \$\endgroup\$tucuxi– tucuxi2020年06月18日 14:20:50 +00:00Commented Jun 18, 2020 at 14:20
-
\$\begingroup\$ 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) \$\endgroup\$2020年06月18日 14:28:05 +00:00Commented Jun 18, 2020 at 14:28
-
2\$\begingroup\$ 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 \$\endgroup\$Charlie Harding– Charlie Harding2020年06月19日 00:40:02 +00:00Commented Jun 19, 2020 at 0:40
-
3\$\begingroup\$ It seems like the regex wins out by 5-10% (jsperf.com/reverse-brackets) \$\endgroup\$Charlie Harding– Charlie Harding2020年06月19日 00:59:17 +00:00Commented Jun 19, 2020 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; }
Explore related questions
See similar questions with these tags.