1

Is there simple function to swapping opening/closing (directional) characters, such as (, ], <, etc.? For example:

>>> swap_direction("abcd{1>2(3]")
"abcd}1<2)3["

Alternatively, perhaps there is a method like str.upper() and str.lower() which converts all directional characters to either their opening or closing variant. This might look like:

>>> to_opening("abcd{1>2(3]")
"abcd}1<2)3["
>>> to_closing("abcd{1>2(3]")
"abcd}1>2(3]"

Is there an easier way besides chaining a bunch of str.replace() calls together?

Antyos
  • 455
  • 5
  • 11
  • 3
    Python's docs have lists of both [the built-in functions](https://docs.python.org/3/library/functions.html) and [the methods on string objects](https://docs.python.org/3/library/stdtypes.html#string-methods). See also e.g. https://stackoverflow.com/q/3411771/3001761. – jonrsharpe May 20 '21 at 15:49
  • 2
    well, you an use `str.translate` in this case but you still need to create the mapping yourself, – juanpa.arrivillaga May 20 '21 at 16:01
  • Thanks @juanpa.arrivillaga and @jonrsharpe! Between both of your comments I was able to come up with a pretty solid function for each of the cases I mentioned in the original question. – Antyos May 21 '21 at 01:02

2 Answers2

2

Tl;dr

Based on replies by @jonrsharpe and @juanpa.arrivillaga, I came up with the following functions to be the best answer:

open_chars = "([{<"
close_chars = ")]}>"

swap_direction_table = str.maketrans(open_chars+close_chars, open_chars+close_chars)

def to_opening(text):
    for ch_close, ch_open in zip(close_chars, open_chars):
        if ch_close in text:
            text = text.replace(ch_close, ch_open)
    return text


def to_closing(text):
    for ch_open, ch_close in zip(open_chars, close_chars):
        if ch_open in text:
            text = text.replace(ch_open, ch_close)
    return text


def swap_direction(text):
    return s.translate(swap_direction_table)

Additional Testing

For the sake of thoroughness, I did a few additional tests. Below is the complete list of functions I tried, organized by technique.

# sandbox.py

open_chars = "([{<"
close_chars = ")]}>"

to_open_table = str.maketrans(close_chars, open_chars)
to_close_table = str.maketrans(open_chars, close_chars)
swap_direction_table = str.maketrans(open_chars+close_chars, close_chars+open_chars)

def to_opening_tr(text):
    return text.translate(to_open_table)
    
def to_closing_tr(text):
    return text.translate(to_close_table)

def swap_direction_tr(text):
    return text.translate(swap_direction_table)

                     
def to_opening_tb(text):
    return text.translate(str.maketrans(close_chars, open_chars))

def to_closing_tb(text):
    return text.translate(str.maketrans(open_chars, close_chars))

def swap_direction_tb(text):
    return text.translate(str.maketrans(open_chars+close_chars, close_chars+open_chars))


def to_opening_r(text):
    return text.replace(")", "(").replace("]", "[").replace("}", "{").replace(">", "<")

def to_closing_r(text):
    return text.replace("(", ")").replace("[", "]").replace("{", "}").replace("<", ">")
                                                                

def to_opening_fr(text):
    for ch_close, ch_open in zip(close_chars, open_chars):
        if ch_close in text:
            text = text.replace(ch_close, ch_open)
    return text

def to_closing_fr(text):
    for ch_open, ch_close in zip(open_chars, close_chars):
        if ch_open in text:
            text = text.replace(ch_open, ch_close)
    return text

def swap_direction_fr(text):
    for ch_open, ch_close in zip(open_chars, close_chars):
        if ch_open in text and ch_close in text:
            text = text.replace(ch_open, '$temp$').replace(ch_close, ch_open).replace('$temp$', ch_close)
        elif ch_open in text:
            text = text.replace(ch_open, ch_close)
        elif ch_close in text:
            text = text.replace(ch_close, ch_open)

    return text

Here are the results from the timeit library:

to_opening_tr:     500000 loops, best of 5: 622 nsec per loop
to_opening_tb:     500000 loops, best of 5: 784 nsec per loop
to_opening_r:      1000000 loops, best of 5: 293 nsec per loop
to_opening_fr:     500000 loops, best of 5: 600 nsec per loop

to_closing_tr:     500000 loops, best of 5: 622 nsec per loop
to_closing_tb:     500000 loops, best of 5: 800 nsec per loop
to_closing_r:      1000000 loops, best of 5: 328 nsec per loop
to_closing_fr:     500000 loops, best of 5: 582 nsec per loop

swap_direction_tr: 500000 loops, best of 5: 569 nsec per loop
swap_direction_tb: 200000 loops, best of 5: 998 nsec per loop
swap_direction_fr: 500000 loops, best of 5: 937 nsec per loop

I used the following PowerShell script to automate the process a bit:

$test_str = "'abcd{1>2(3]'"
# $test_str = "'abcd{1>2(3]abcd}1>)))3[[[[abasdh[[[[[[[['" # Alternate string to try

Write-Host "to_opening_tr:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_opening_tr($test_str)"

Write-Host "to_opening_tb:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_opening_tb($test_str)"

Write-Host "to_opening_r:      " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_opening_r($test_str)"

Write-Host "to_opening_fr:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_opening_fr($test_str)"

Write-Host ""

Write-Host "to_closing_tr:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_closing_tr($test_str)"

Write-Host "to_closing_tb:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_closing_tb($test_str)"

Write-Host "to_closing_r:      " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_closing_r($test_str)"

Write-Host "to_closing_fr:     " -NoNewline
python -m timeit -s "import sandbox" "sandbox.to_closing_fr($test_str)"

Write-Host ""

Write-Host "swap_direction_tr: " -NoNewline
python -m timeit -s "import sandbox" "sandbox.swap_direction_tr($test_str)"

Write-Host "swap_direction_tb: " -NoNewline
python -m timeit -s "import sandbox" "sandbox.swap_direction_tb($test_str)"

Write-Host "swap_direction_fr: " -NoNewline
python -m timeit -s "import sandbox" "sandbox.swap_direction_fr($test_str)"

I don't want to hear about how my PowerShell sucks ;)

The algorithms with the best times in each category are the ones in the Tl;dr above. The results seem to be consistent among a few different sample strings I've tried.

Antyos
  • 455
  • 5
  • 11
0
MAP1 = {'}': '{', ')': '(', ']': '[', '>': '<'}
MAP2 = dict([(MAP1[k], k) for k in MAP1])
MAP3 = MAP1 + MAP2


def to_opening(s):
    res = ''
    for i in s:
        res += MAP1.get(i, i)
    return res


def to_closing(s):
    res = ''
    for i in s:
        res += MAP2.get(i, i)
    return res


def swap_direction(s):
    res = ''
    for i in s:
        res += MAP3.get(i, i)
    return res
Vad Sim
  • 266
  • 8
  • 21