2

I have been trying to figure this out for a while now and i just cannot figure out why my code only displays the first use of the word.If someone could suggest a quick and easy way that it would display both then that would be greatly appreciated.

sentence = input("please insert a sentence").lower()
keyword = input ("please insert a keyword you would like to search for")
splitsentence = sentence.split(" ")
position = sentence.index(keyword)
if keyword in sentence:
    print(position)
Luke Evans
  • 39
  • 4
  • 8
    Because that's what the `index` function is meant to do. – barak manos Feb 13 '17 at 13:09
  • @Chris_Rands: this is not a list, but a string and it is a *substring*... But probably there is a dupe somewhere. – Willem Van Onsem Feb 13 '17 at 13:26
  • @WillemVanOnsem You're right, was fooled because the OP makes a list but dosen't use it, perhaps this instead: http://stackoverflow.com/questions/3873361/finding-multiple-occurrences-of-a-string-within-a-string-in-python – Chris_Rands Feb 13 '17 at 13:29

2 Answers2

1

That's how index() is designed; to only return the first occurrence.

If you want to find all occurrences, you must call it multiple times in a loop or with recursion. Before we do that, you should know that you can provide start and end parameters to define where to search in the sentence:

>>> "cat cat".index("cat")
0
>>> "cat cat".index("cat", 1)  # start search from index 1
4
>>> "cat cat".index("cat", 1, 4)  # ...and stop search right before index 4
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: substring not found

This way we can avoid getting the same index all the time in our loop:

s = "cat cat"
indexes = []

index = -1
while True:
    try:
        index = s.index("cat", index + 1)
    except ValueError:
        break
    indexes.append(index)

print(indexes)
# Output: [0, 4]

Here's a recursive generator if you want to go fancy:

def find_indices(s, sub, start=0, end=None):
    if end is None:
        end = len(s)
    try:
        start = s.index(sub, start, end)
    except ValueError:
        return
    yield start
    yield from find_all_indexes(s, sub, start + 1, end)

Usage (also still supports start and end arguments):

>>> list(find_indices("cat cat cat cat", "cat"))
[0, 4, 8, 12]

Or if you want a non-recursive generator, you could use the original while loop and have it yield instead of append to a list:

def find_indices(s, sub, start=0, end=None):
    if end is None:
        end = len(s)
    start -= 1
    while True:
        try:
            start = s.index(sub, start + 1, end)
        except ValueError:
            break
        yield start

Exact same usage as before.

Markus Meskanen
  • 19,939
  • 18
  • 80
  • 119
0

Because that is how .index is defined: the documentation says:

str.index(sub[, start[, end]])

Like find(), but raise ValueError when the substring is not found.

and .find:

str.find(sub[, start[, end]])

Return the lowest index in the string where substring sub is found within the slice s[start:end]. Optional arguments start and end are interpreted as in slice notation. Return -1 if sub is not found.

(highlighting added)

An idea to find all indices is to use:

[i for i in range(len(sentence)) if keyword == sentence[i:i+len(keyword)]]

Or perhaps more efficient: a generator that generates indices:

def find_indices(sentence,keyword):
    idx = -1
    while True:
        idx = sentence.find(keyword,idx+1)
        if idx < 0:
            break
        yield idx
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555