909

I use the following to check if item is in my_list:

if item in my_list:
    print("Desired item is in list")

Is "if item in my_list:" the most "pythonic" way of finding an item in a list?

EDIT FOR REOPENING: the question has been considered dupplicate, but I'm not entirely convinced: here this question is roughly "what is the most Pythonic way to find an element in a list". And the first answer to the question is really extensive in all Python ways to do this.

Whereas on the linked dupplicate question and its corresponding answer, the focus is roughly only limited to the 'in' key word in Python. I think it is really limiting, compared to the current question.

And I think the answer to this current question, is more relevant and elaborated that the answer of the proposed dupplicate question/answer.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • 3
    That's perfectly fine and should work if item equals one of the elements inside `myList`. – Niklas B. Mar 03 '12 at 02:06
  • 1
    do you mean it was the good way to do things ? in my several trials, maybe there was whitespaces, and line feeds intereferring... i just wanted to be sure it is the good way to implement "find in list" (in general) – Stephane Rolland Mar 03 '12 at 02:09
  • 1
    Amazing that searching on how to EXTRACT a SUBSET from a list based on a condition did not find this question and its fine answers. Perhaps adding this comment will allow it to hit on the words extract and/or subset, the next time someone searches using those terms. Cheers. – johnjps111 Oct 29 '21 at 14:16
  • Rolled back the last revision because it the accepted answer was based on the previous version. – Gert Arnold Aug 13 '22 at 21:08
  • This is an inferior version of the question because "sometimes, it doesn't find the item." wasn't clear. For example, many people expect `'x' in [['a', 'b'], ['x', 'y']]` to search the nested list and evaluate to `True`, which of course it doesn't (`x` is neither equal to `['a', 'b']` nor `['x', 'y']`). Since there was never a [mre] explaining "sometimes", we can't know what problem was intended to be solved. For the simple case, the linked duplicate asks the question much better, and has authoritative answers. – Karl Knechtel Jan 23 '23 at 02:43
  • 1
    @johnjps111 that's partly because the top answer here answered a bunch of unasked questions on speculation. That's not how Stack Overflow is intended to work; it's **not a discussion forum**. That said "extract a subset" sounds to me like a **very strange** way to describe the process of figuring out which elements of a list meet a condition. – Karl Knechtel Jan 23 '23 at 02:45

14 Answers14

1712

As for your first question: "if item is in my_list:" is perfectly fine and should work if item equals one of the elements inside my_list. The item must exactly match an item in the list. For instance, "abc" and "ABC" do not match. Floating point values in particular may suffer from inaccuracy. For instance, 1 - 1/3 != 2/3.

As for your second question: There's actually several possible ways if "finding" things in lists.

Checking if something is inside

This is the use case you describe: Checking whether something is inside a list or not. As you know, you can use the in operator for that:

3 in [1, 2, 3] # => True

Filtering a collection

That is, finding all elements in a sequence that meet a certain condition. You can use list comprehension or generator expressions for that:

matches = [x for x in lst if fulfills_some_condition(x)]
matches = (x for x in lst if x > 6)

The latter will return a generator which you can imagine as a sort of lazy list that will only be built as soon as you iterate through it. By the way, the first one is exactly equivalent to

matches = filter(fulfills_some_condition, lst)

in Python 2. Here you can see higher-order functions at work. In Python 3, filter doesn't return a list, but a generator-like object.

Finding the first occurrence

If you only want the first thing that matches a condition (but you don't know what it is yet), it's fine to use a for loop (possibly using the else clause as well, which is not really well-known). You can also use

next(x for x in lst if ...)

which will return the first match or raise a StopIteration if none is found. Alternatively, you can use

next((x for x in lst if ...), [default value])

Finding the location of an item

For lists, there's also the index method that can sometimes be useful if you want to know where a certain element is in the list:

[1,2,3].index(2) # => 1
[1,2,3].index(4) # => ValueError

However, note that if you have duplicates, .index always returns the lowest index:......

[1,2,3,2].index(2) # => 1

If there are duplicates and you want all the indexes then you can use enumerate() instead:

[i for i,x in enumerate([1,2,3,2]) if x==2] # => [1, 3]
Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • i have read a lot of pages complaining about the absence of **find built-in function** for list in python... so you tend to aggre that (if x in list:) is the correct pythonic way of doing a find in a list in python ? – Stephane Rolland Mar 03 '12 at 02:13
  • @Stephane: For what purpose? `if x in list` will just check the presence of an item. If you want to actually "find" something matching a certain criteria, you have list comprehensions/generator expressions, which are really powerful. – Niklas B. Mar 03 '12 at 02:15
  • for what purpose ? because i am an old c++ man and i'm stuck with the concept of finding items in lists in my head... so i want to be sure how to do this in python... from what i have read i love list comprehension, but they do things when they find things. – Stephane Rolland Mar 03 '12 at 02:19
  • 15
    Stephane: Let me rephrase it: `if x in list` is *not* the thing that people complain not being a built-in function. They complain about the fact that there is not explicit way to find the first occurrence of something in a list that matches a certain condition. But as stated in my answer, `next()` can be (ab)used for that. – Niklas B. Mar 03 '12 at 02:21
  • okay, so fulfills_some_condition can be a predicate, i really like this, just in the c++ STL-algorithm way of doing things. is it prefered the list way or the tuple way ? – Stephane Rolland Mar 03 '12 at 02:27
  • 4
    @Stephane: The second one does not generate a tuple, but a generator (which is a not-yet-built list, basically). If you want to use the result only once, a generator is usually preferrable. However, if you want to use the created collection several times afterwards, it's advisable to create an explicit list in the first place. Have a look at my update, it's now a bit better structured :) – Niklas B. Mar 03 '12 at 02:30
  • 45
    Your "finding first occurrence" example is golden. Feels more pythonic than the `[list comprehension...][0]` approach – acjay Mar 03 '13 at 15:52
  • 6
    I am more and more dissiapointed with python 'functional' capabilities. In haskell there is find function in Data.List module that doing exactly that. But in python it's not and it's to small to make it a library so you have to reimplement the same logic over and over again. What a waste... – user1685095 Jan 09 '16 at 19:19
  • `next` takes an iterator as the first parameter and a list/tuple is NOT an iterator. So the list comprehension part should be wrapped with `iter()`. see http://stackoverflow.com/questions/25398201#25398201 – Devy Mar 28 '16 at 15:49
  • @Devy are you referring to one of the code samples in this answer? Both use a generator expression as the first argument. – Niklas B. Mar 28 '16 at 21:05
  • 5
    It would be nice if there was a kwarg to `index()` called `key` that worked like the `key` accepted by `max()`; for example: `index(list, key=is_prime)`. – Curt Aug 16 '16 at 03:26
  • what would be the best way to find the index of the first item matching a certain condition? e.g. in a list of strings, return the index of the first string that starts with a '*' – Manuela Hutter Apr 28 '17 at 19:19
  • An Addition to this answer: If you want all positions of an element `e` in a part of a list `L` limited by two indices `start` and `end` you can modify the last expression like this `[i for i,x in enumerate(L) if x==e and start<=i<=end]` – Khris May 31 '17 at 08:25
  • 1
    Great answer! See also [my answer](https://stackoverflow.com/a/48140611/237105) below for an alternative approach to finding the first occurrence. – Antony Hatchkins Jan 07 '18 at 19:08
  • @Curt I found a nice way to abuse the system to do such a thing. In order to find the index of the first item that fits a criteria, for a list called things. `index = things.index(next(thing for thing in things if meets_criteria(thing))` – Eredea Jun 24 '20 at 18:36
  • If you are only interested whether such a "positive" element exists, instead of `if not next((x for x in lst if ...), False)` or some such, `if not any(x for x in lst if ...)` can be a bit more readable. – heiner Jan 26 '22 at 20:10
267

If you want to find one element or None use default in next, it won't raise StopIteration if the item was not found in the list:

first_or_default = next((x for x in lst if ...), None)
Janusz Skonieczny
  • 17,642
  • 11
  • 55
  • 63
  • 2
    `next` takes an iterator as the first parameter and a list/tuple is NOT an iterator. So it should be `first_or_default = next(iter([x for x in lst if ...]), None)` see https://docs.python.org/3/library/functions.html#next – Devy Mar 28 '16 at 15:45
  • 19
    @Devy: that's right, but `(x for x in lst if ...)` is a generator over the list `lst` (which *is* an iterator). If you do `next(iter([x for x in lst if ...]), None)`, you have to construct the list `[x for x in lst if ...]`, which will be a much more expensive operation. – Erlend Graff Apr 20 '16 at 07:12
  • 2
    There is an abstraction in here to define a find function. Just encapsulate the the boolean expession of the `if` in a lambda & you can write `find(fn,list)` usually instead of obfuscating generator code. – semiomant Mar 29 '17 at 08:10
33

While the answer from Niklas B. is pretty comprehensive, when we want to find an item in a list it is sometimes useful to get its index:

next((i for i, x in enumerate(lst) if [condition on x]), [default value])
Vincent Cantin
  • 16,192
  • 2
  • 35
  • 57
29

Finding the first occurrence

There's a recipe for that in itertools:

def first_true(iterable, default=False, pred=None):
    """Returns the first true value in the iterable.

    If no true value is found, returns *default*

    If *pred* is not None, returns the first item
    for which pred(item) is true.

    """
    # first_true([a,b,c], x) --> a or b or c or x
    # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
    return next(filter(pred, iterable), default)

For example, the following code finds the first odd number in a list:

>>> first_true([2,3,4,5], None, lambda x: x%2==1)
3  

You can copy/paste it or install more-itertools

pip3 install more-itertools

where this recipe is already included.

Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
  • 1
    Thanks. It's a recipe, but you have to copy and paste that code into your own, which is incredibly dumb. Why didn't they just include it? Ruby has `Enumerable#find` which is a classic example of how the user-friendliness of its libraries are light-years ahead of Python's. – Adam Spiers Jun 22 '21 at 15:23
  • 1
    @AdamSpiers `pip install more-itertools` – Antony Hatchkins Jun 22 '21 at 17:33
  • 3
    Thanks, I guess you mean `more-itertools.first_true()`. Good to know about this, but it's still beyond ridiculous that there is not an elegant way to achieve this natively with the language or standard library. The `next` hack requiring a default is cumbersome. – Adam Spiers Jun 25 '21 at 10:59
  • @AdamSpiers Afaik they didn't want python to turn into lisp or haskell. Having complete range of functional tools would make programs written in python as hard to read as in functional languages. Yet I personally also miss those functions in the language or in the standard lib. – Antony Hatchkins Jun 28 '21 at 06:53
  • 2
    If that's the rationale they used, it doesn't make any sense at all. Ruby didn't turn into Lisp or Haskell by virtue of including the most commonly needed methods in its standard library, and anyway, IMHO functional languages can be perfectly readable, and often more so than imperative languages. But anyway I shouldn't trigger a language war here ;-) – Adam Spiers Jun 28 '21 at 11:12
  • 1
    @AdamSpiers I'm not 100% sure they didn't have other motives, it's just the only rationale I'm aware of. I find ruby syntax less readable than that of python. You know, if you include all the keywords from functional languages the next question will be 'why exactly the same construct runs x times slower in python than in haskell'. Not including them is just a hint that if you like them, maybe python is the wrong language to write them with ;) Readability depends on the writer in the first place. Python only strives to make the life of people who like to write unreadable code a bit harder :) – Antony Hatchkins Jun 29 '21 at 07:58
10

Another alternative: you can check if an item is in a list with if item in list:, but this is order O(n). If you are dealing with big lists of items and all you need to know is whether something is a member of your list, you can convert the list to a set first and take advantage of constant time set lookup:

my_set = set(my_list)
if item in my_set:  # much faster on average than using a list
    # do something

Not going to be the correct solution in every case, but for some cases this might give you better performance.

Note that creating the set with set(my_list) is also O(n), so if you only need to do this once then it isn't any faster to do it this way. If you need to repeatedly check membership though, then this will be O(1) for every lookup after that initial set creation.

Engineero
  • 12,340
  • 5
  • 53
  • 75
6

Definition and Usage

the count() method returns the number of elements with the specified value.

Syntax

list.count(value)

example:

fruits = ['apple', 'banana', 'cherry']

x = fruits.count("cherry")

Question's example:

item = someSortOfSelection()

if myList.count(item) >= 1 :

    doMySpecialFunction(item)
josef
  • 872
  • 9
  • 8
4

You may want to use one of two possible searches while working with list of strings:

  1. if list element is equal to an item ('example' is in ['one','example','two']):

    if item in your_list: some_function_on_true()

    'ex' in ['one','ex','two'] => True

    'ex_1' in ['one','ex','two'] => False

  2. if list element is like an item ('ex' is in ['one,'example','two'] or 'example_1' is in ['one','example','two']):

    matches = [el for el in your_list if item in el]

    or

    matches = [el for el in your_list if el in item]

    then just check len(matches) or read them if needed.

Alexey Antonenko
  • 2,389
  • 1
  • 18
  • 18
4

If you are going to check if value exist in the collectible once then using 'in' operator is fine. However, if you are going to check for more than once then I recommend using bisect module. Keep in mind that using bisect module data must be sorted. So you sort data once and then you can use bisect. Using bisect module on my machine is about 12 times faster than using 'in' operator.

Here is an example of code using Python 3.8 and above syntax:

import bisect
from timeit import timeit

def bisect_search(container, value):
    return (
      (index := bisect.bisect_left(container, value)) < len(container) 
      and container[index] == value
    )

data = list(range(1000))
# value to search
true_value = 666
false_value = 66666

# times to test
ttt = 1000

print(f"{bisect_search(data, true_value)=} {bisect_search(data, false_value)=}")

t1 = timeit(lambda: true_value in data, number=ttt)
t2 = timeit(lambda: bisect_search(data, true_value), number=ttt)

print("Performance:", f"{t1=:.4f}, {t2=:.4f}, diffs {t1/t2=:.2f}")

Output:

bisect_search(data, true_value)=True bisect_search(data, false_value)=False
Performance: t1=0.0220, t2=0.0019, diffs t1/t2=11.71
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
  • technically only partially answers the original question, but still my favourite answer here because `bisect.bisect_left/right(..., key=...)` solves many related questions that link to this page. – casper.dcl Sep 15 '22 at 00:32
4

for_loop

def for_loop(l, target):
    for i in l:
        if i == target:
            return i
    return None


l = [1, 2, 3, 4, 5]
print(for_loop(l, 0))
print(for_loop(l, 1))
# None
# 1

next

def _next(l, target):
    return next((i for i in l if i == target), None)


l = [1, 2, 3, 4, 5]
print(_next(l, 0))
print(_next(l, 1))
# None
# 1

more_itertools

more_itertools.first_true(iterable, default=None, pred=None)

install

pip install more-itertools

or use it directly

def first_true(iterable, default=None, pred=None):
    return next(filter(pred, iterable), default)
from more_itertools import first_true

l = [1, 2, 3, 4, 5]
print(first_true(l, pred=lambda x: x == 0))
print(first_true(l, pred=lambda x: x == 1))
# None
# 1

Compare

method time/s
for_loop 2.77
next() 3.64
more_itertools.first_true() 3.82 or 10.86
import timeit
import more_itertools


def for_loop():
    for i in range(10000000):
        if i == 9999999:
            return i
    return None


def _next():
    return next((i for i in range(10000000) if i == 9999999), None)


def first_true():
    return more_itertools.first_true(range(10000000), pred=lambda x: x == 9999999)


def first_true_2():
    return more_itertools.first_true((i for i in range(10000000) if i == 9999999))


print(timeit.timeit(for_loop, number=10))
print(timeit.timeit(_next, number=10))
print(timeit.timeit(first_true, number=10))
print(timeit.timeit(first_true_2, number=10))
# 2.7730861
# 3.6409407000000003
# 10.869996399999998
# 3.8214487000000013
XerCis
  • 917
  • 7
  • 6
  • 1
    I've done the same test as above, but also additionally added: ```python def first_true_2(): return more_itertools.first_true((i for i in range(10_000_000) if i == 9_999_999)) ``` ... which actually ends up with the fastest time, or at least comparable within the variation. My _opinion_ is that `first_true` has an easier method interface to read and readability is important. – MrSpaceman Jan 26 '22 at 09:51
3

Instead of using list.index(x) which returns the index of x if it is found in list or returns a #ValueError message if x is not found, you could use list.count(x) which returns the number of occurrences of x in the list (validation that x is indeed in the list) or it returns 0 otherwise (in the absence of x). The cool thing about count() is that it doesn't break your code or require you to throw an exception when x is not found.

1

you said that in my several trials, maybe there were whitespaces, and line feeds interfering .that why I m giving you this solution.

myList=[" test","ok","ok1"]
item = "test"#someSortOfSelection()
if  True in list(map(lambda el : item in el ,myList)):
    doMySpecialFunction(item)
Bahae El Hmimdi
  • 364
  • 1
  • 5
0

Check there are no additional/unwanted whites space in the items of the list of strings. That's a reason that can be interfering explaining the items cannot be found.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
0
 lstr=[1, 2, 3]
 lstr=map(str,lstr)
 r=re.compile('^(3){1}')
 results=list(filter(r.match,lstr))
 print(results)
Golden Lion
  • 3,840
  • 2
  • 26
  • 35
0

in works with a list() of dict()s too:

a = [ {"a":1}, {"b":1, "c":1} ]

b = {"c":1 , "b":1} # <-- No matter the order
    
if b in a: 
    print("b is in a")

At least in Python 3.8.10, no matter the order

Emeeus
  • 5,072
  • 2
  • 25
  • 37