57

For lists, the method list.index(x) returns the index in the list of the first item whose value is x. But if I want to look inside the list items, and not just at the whole items, how do I make the most Pythoninc method for this?

For example, with

l = ['the cat ate the mouse',
     'the tiger ate the chicken',
     'the horse ate the straw']

this function would return 1 provided with the argument tiger.

11 Answers 11

43

A non-slicky method:

def index_containing_substring(the_list, substring):
    for i, s in enumerate(the_list):
        if substring in s:
              return i
    return -1
Sign up to request clarification or add additional context in comments.

1 Comment

Python is such a great language and has no builtin support for such things ? 😴️
41

With a one-liner:

index = [idx for idx, s in enumerate(l) if 'tiger' in s][0]

2 Comments

It does not run if there are NA's. How to consider this case?
Not sure what you mean by "NA's", but if you're talking about the behaviour when the substring (e.g. "tiger") is missing – you could use a default value like -1 in the other answer by rewriting the expression as follows: ([idx for idx, s in enumerate(l) if 'tiger' in s] + [-1])[0]. That way, if the substring is found, the generator list expression is non-empty, otherwise the first/only element in the list within the round brackets will be the fallback value of -1.
9

Variation of abyx solution (optimised to stop when the match is found)

def first_substring(strings, substring):
    return next(i for i, string in enumerate(strings) if substring in string)

If you are pre 2.6 you'll need to put the next() at the end

def first_substring(strings, substring):
    return (i for i, string in enumerate(strings) if substring in string).next()

Comments

3
def find(l, s):
    for i in range(len(l)):
        if l[i].find(s)!=-1:
            return i
    return None # Or -1

Comments

3

This is quite slick and fairly efficient.

>>> def find(lst, predicate):
...     return (i for i, j in enumerate(lst) if predicate(j)).next()
... 
>>> l = ['the cat ate the mouse','the tiger ate the chicken','the horse ate the straw']
>>> find(l, lambda x: 'tiger' in x)
1

Only problem is that it will raise StopIteration if the item is not found (though that is easily remedied).

1 Comment

StopIteration can be avoided: return next((i for i,j in enumerate(lst) if predicate(j)), -1) (Python 2.6+)
2
def first_substring(strings, substring):
    return min(i for i, string in enumerate(strings) if substring in string)

Note: This will raise ValueError in case no match is found, which is better in my opinion.

3 Comments

Fancy but not efficient, as it tests all elements of the list regardless of whether the text has been found previously or not. Also, Python's 'something'.find(s) function returns -1 when no match is found, so I'd call that Pythonic.
Does not work, at least in Python 2.6. You can't use both an iterable and an extra argument in min(). @Etiene: this is a generator expression, not a list comprehension, so it wouldn't generate everything.
@Etienne - premature optimization is the root of all evil etc. @Max - you are correct, fixed.
2

imho with this line, you'll find only the first occurence without processing the whole list

index = next((i for i in enumerate(l) if "tiger" in i[1]),[-1,-1])[0]

Comments

1

I wanted to just get the text and not raise exception if item was not found

search = 'a'
next((s for s in ["aa",'c'] if search in s), None)

search = 'b'
next((el for el in ["aa",'c'] if search in el), None)

It's one of those things I wish was just implemented natively.

Comments

0
  >>> li = ['my','array','with','words']
  >>> reduce(lambda tup, word: (tup[0], True) if not tup[1] and word  == 'my' else (tup[0]+1 if not tup[1] else tup[0], tup[1]), li, (0, False))[0]
  0
  >>> reduce(lambda tup, word: (tup[0], True) if not tup[1] and word  == 'words' else (tup[0]+1 if not tup[1] else tup[0], tup[1]), li, (0, False))[0]
  3

Comments

0

Using map function:

index = np.nonzero(map(lambda x: substring in x, strings))[0][0]

Comments

0

@kennytm offers a great answer that helped me; to build from theirs a function that allows regex, I wrote:

def substringindex(inputlist, inputsubstring):
    s = [x for x in inputlist if re.search(inputsubstring, x)]

    if s != []:
        return (inputlist.index(s[0]), s[0])
    return -1

This function works exactly like theirs, but supports regex.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.