7

I would like to find the last occurrence of a number of characters in a string.

str.rfind() will give the index of the last occurrence of a single character in a string, but I need the index of the last occurrence of any of a number of characters. For example if I had a string:

test_string = '([2+2])-[3+4])'

I would want a function that returns the index of the last occurence of {, [, or { similar to

test_string.rfind('(', '[', '{')

Which would ideally return 8. What is the best way to do this?

max(test_string.rfind('('), test_string.rfind('['), test_string.rfind('{'))

seems clunky and not Pythonic.

capt-calculator
  • 722
  • 5
  • 12
  • 1
    I don't like where this question is going.... are you trying to write a math parser using this type of character matching? You should use [`pyparsing`](https://pythonhosted.org//pyparsing/) it is a far more robust library for parsing strings with arbitrarily nested enclosing character pairs – Cory Kramer Aug 11 '15 at 19:49
  • You could use a regex to find one of those characters with as much as possible before that: `re.match(r".*[\(\[\}]", test_string).end() - 1` Not pretty, but seems to work. – tobias_k Aug 11 '15 at 19:52
  • 1
    `max(map(test_string.rfind,'([{'))` – Mazdak Aug 11 '15 at 19:57
  • @CoryKramer yes this is for math parsing. In normal circumstances I would use pyparsing, but I'm doing this more for educational purposes and trying to implement something on my own. – capt-calculator Aug 12 '15 at 19:49

4 Answers4

9

You can use generator expression to do this in a Pythonic way.

max(test_string.rfind(i) for i in "([{")

This iterates through the list/tuple of characters that you want to check and uses rfind() on them, groups those values together, and then returns the maximum value.

michaelpri
  • 3,521
  • 4
  • 30
  • 46
2

This is pretty concise, and will do the trick.

max(map(test_string.rfind, '([{'))
pzp
  • 6,249
  • 1
  • 26
  • 38
1

You can use reversed to start at the end of the string getting the first match, using the length of the string -1 - the index i to get the index counting from the start, doing at worst a single pass over the string:

test_string = '([2+2])-[3+4])'
st = {"[", "(", "{"}
print(next((len(test_string) - 1 - i 
            for i, s in enumerate(reversed(test_string)) if s in st),-1))
8 

If there is no match, you will get -1 as the default value. This is a lot more efficient if you a large amount of substrings to search for than doing an O(n) rfind for every substring you want to match and then getting the max of all those

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
0
>>> def last_of_many(string, findees):
...     return max(string.rfind(s) for s in findees)
... 
>>> test_string = '([2+2])-[3+4])'
>>> last_of_many(test_string, '([{')
8
>>> last_of_many(test_string, ['+4', '+2'])
10
>>> 
Cyphase
  • 11,502
  • 2
  • 31
  • 32