I would not return 0, since it can be a starting index, or use -1, not one or the other value that is not possible, you can just use try / except and return the index:
def get_ind(s, targ): s = s.lower() for t in targets: try: return s.index(t.lower()) except ValueError: pass return None
If you want to ignore case for input string, then also set s = s.lower()
before the loop.
You can also do something like:
def get_ind_next(s, targ): s = s.lower() return next((s.index(t) for t in map(str.lower,targ) if t in s), None)
But this does in the worst case, two searches for each substring, and not for one with try / except. It will be at least also a short circuit in the first match.
If you really want the min of all to change to:
def get_ind(s, targ): s = s.lower() mn = float("inf") for t in targ: try: i = s.index(t.lower()) if i < mn: mn = i except ValueError: pass return mn def get_ind_next(s, targ): s = s.lower() return min((s.index(t) for t in map(str.lower, targ) if t in s), default=None)
default=None
only works in python> = 3.4, so if you are using python2 you have to change the logic a bit.
Python3 terms:
In [29]: s = "hello world" * 5000 In [30]: s += "grea" + s In [25]: %%timeit ....: targ = [re.escape(x) for x in targets] ....: pattern = r"%(pattern)s" % {'pattern' : "|".join(targ)} ....: firstMatch = next(re.finditer(pattern, s, re.IGNORECASE),None) ....: if firstMatch: ....: pass ....: 100 loops, best of 3: 5.11 ms per loop In [18]: timeit get_ind_next(s, targets) 1000 loops, best of 3: 691 µs per loop In [19]: timeit get_ind(s, targets) 1000 loops, best of 3: 627 µs per loop In [20]: timeit min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0]) 1000 loops, best of 3: 1.03 ms per loop In [21]: s = 'Iamfoothegreat' In [22]: targets = ['bar', 'grea', 'other','foo'] In [23]: get_ind_next(s, targets) == get_ind(s, targets) == min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0]) Out[24]: True
python2:
In [13]: s = "hello world" * 5000 In [14]: s += "grea" + s In [15]: targets = ['foo', 'bar', 'grea', 'other'] In [16]: timeit get_ind(s, targets)1000 loops, best of 3: 322 µs per loop In [17]: timeit min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0]) 1000 loops, best of 3: 710 µs per loop In [18]: get_ind(s, targets) == min([s.lower().find(x.lower()) for x in targets if x.lower() in s.lower()] or [0]) Out[18]: True
You can also combine the first with min:
def get_ind(s, targ): s,mn = s.lower(), None for t in targ: try: mn = s.index(t.lower()) yield mn except ValueError: pass yield mn
Which does the same job, it's a little better and can be a little faster:
In [45]: min(get_ind(s, targets)) Out[45]: 55000 In [46]: timeit min(get_ind(s, targets)) 1000 loops, best of 3: 317 µs per loop