Indices of matching parentheses in Python

Question:

Is there a way to get indices of matching parentheses in a string? For example for this one:

text = 'aaaa(bb()()ccc)dd'

I’d like to get a dictionary with values:

result = {4:14, 7:8, 9:10}

which means that parentheses on index 4 and 14 are matching , 7 and 8 an so on.
Thanks a lot.

Asked By: Peter Gubik

||

Answers:

The standard way to check for balanced brackets is to use a stack. In Python, this can be done by appending to and popping from a standard list:

text = 'aaaa(bb()()ccc)dd'
istart = []  # stack of indices of opening parentheses
d = {}

for i, c in enumerate(text):
    if c == '(':
         istart.append(i)
    if c == ')':
        try:
            d[istart.pop()] = i
        except IndexError:
            print('Too many closing parentheses')
if istart:  # check if stack is empty afterwards
    print('Too many opening parentheses')
print(d)

Result:

In [58]: d
Out[58]: {4: 14, 7: 8, 9: 10}
Answered By: Bas Swinckels

You mean an automated way?
I don’t think so.

You need to create a program using a stack, in which you push the index when you find an open parenthesis, and pop it when you find a closing parenthesis.

In Python, you can easily use a list as a stack, since they have the append() and pop() methods.

def find_parens(s):
    toret = {}
    pstack = []

    for i, c in enumerate(s):
        if c == '(':
            pstack.append(i)
        elif c == ')':
            if len(pstack) == 0:
                raise IndexError("No matching closing parens at: " + str(i))
            toret[pstack.pop()] = i

    if len(pstack) > 0:
        raise IndexError("No matching opening parens at: " + str(pstack.pop()))

    return toret

Hope this helps.

Answered By: Baltasarq

Try this also.

def match(str):
    stack = []
    ans = {}
    for i,ch in enumerate(str):
        if ch == "(":
            stack.append(i)
        else :
            try:
                if ch == ")":
                    ans[stack.pop()] = i
                    continue
            except:
                return False
            
    if len(stack) > 0:
        return False
    else:
        return ans

test_str = "()"
print(match(test_str))

Answered By: Muriithi Derrick

A bit late to the game but was looking for an alternative/shorter way of finding matching parenthesis….came up with this:

def matching_parentheses(string, idx):
    if idx < len(string) and string[idx] == "(":
        opening = [i for i, c in enumerate(string[idx + 1 :]) if c == "("]
        closing = [i for i, c in enumerate(string[idx + 1 :]) if c == ")"]
        for i, j in enumerate(closing):
            if i >= len(opening) or j < opening[i]:
                return j + idx + 1
    return -1

So you can do something like

result = {}
for i, j in enumerate(text):
    if j=='(':
        result[i] = matching_parentheses(text, i)

print(result)

Which produces

{4: 14, 7: 8, 9: 10}

Edit…here’s an updated version which returns the dict directly:

def matching_parentheses(string):

    opening = [] 
    mydict = {}

    # loop over the string
    for i,c in enumerate(string):

        # new ( found => push it to the stack
        if c == '(':
            opening.append(i)


        # new ) found => pop and create an entry in the dict 
        elif c==')':

            # we found a ) so there must be a ( on the stack
            if not opening:
                return False
            else:
                mydict[opening.pop()] = i

    # return dict if stack is empty
    return mydict if not opening else False

or a more condensed version could be

def matching_parentheses(string):

    op= [] 
    dc = { 
        op.pop() if op else -1:i for i,c in enumerate(string) if 
        (c=='(' and op.append(i) and False) or (c==')' and op)
    }
    return False if dc.get(-1) or op else dc

And now you can do

text = 'aaaa(bb()()ccc)dd'
m = matching_parentheses(text)
print(m)
Answered By: Marcel Swidde

This is what I’m using to find the closing bracket of a dictionary so that I can pull it out of a text string and load it as a json object:

test = 'sdfasdfasdfs{ ffasdf{ daf{}}dsdfds}sdfs'
items = []
start = 0
for i,c in enumerate(test):
    if c =='{':
        if start == 0:
            start = i
            start += 1
        items.append(i)
    if c == '}':
        items.pop()
    if len(items) == 0 and start >0:
        print(f"found the end {c}, {i}")
        break
Answered By: grantr
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.