7

I'm looking for the simplest way how to iterate over the positions of a substring in a string. Currently I'm using a generator for that, but I have a feeling that it's not quite Pythonian:

def iteratePos(haystack, needle) :
    pos = haystack.find(needle)
    while pos >= 0 :
        yield pos
        pos = haystack.find(needle, pos+1)

s = "ABC 11 ABC 111 ABC 1"
for i in iteratePos(s, "ABC") :
    print "aye bee see at", i
    # finds 0, 7, 15

for i in iteratePos(s, "1") :
    print "one at", i
    # finds 4, 5, 11, 12, 13, 19

So, is it possible to do this as a reasonable one-liner? Or should I stick to the solution I have?

(Note: Should there be a simple solution for the case when needle is just one character, I'm interested in that as well.)

yo'
  • 789
  • 10
  • 21

2 Answers2

4
s = "ABC 11 ABC 111 ABC 1"

print([ind for ind,_ in enumerate(s[:-2]) if s[ind:ind+3] == "ABC"])
[0, 7, 15]

In a function:

def iteratePos(haystack, needle) :
    ln = len(needle)
    return [ind for ind,_ in enumerate(s[:-ln-1]) if haystack[ind:ind+ln] == needle]

Or using yield from in Python 3:

def iteratePos(haystack, needle) :
    ln = len(needle)
    yield from (ind for ind, _ in enumerate(haystack[:-ln-1]) if haystack[ind:ind+ln] == needle)
print(next(iteratePos(s,"ABC")))
0
richardec
  • 14,202
  • 6
  • 23
  • 49
Padraic Cunningham
  • 168,988
  • 22
  • 228
  • 312
2

I think your solution is fine ... but I guess you could do

[i for i in range(len(s)) if s[i:].startswith(needle)]

if you really dont like your solution

Joran Beasley
  • 103,130
  • 11
  • 146
  • 174
  • 1
    Ok, this one is quadratic, but it's a nice one, thanks :) For single letters it could be linear tho. Now I think: `[ i for i,c in enumerate(haystack) if c == needle ]` should work for letters. – yo' Jan 31 '15 at 00:11
  • 3
    You can remove the quadratic nature by passing the `start` argument to `startswith`, e.g. `[i for i in range(len(s)) if s.startswith(needle, i)]`. That will avoid materializing all the string slices. – DSM Jan 31 '15 at 01:20
  • 1
    that's awesome I had no idea to do that – Joran Beasley Jan 31 '15 at 01:51