78

Is there clean way to get the value at a list index or None if the index is out or range in Python?

The obvious way to do it would be this:

if len(the_list) > i:
    return the_list[i]
else:
    return None

However, the verbosity reduces code readability. Is there a clean, simple, one-liner that can be used instead?

Chris Dutrow
  • 48,402
  • 65
  • 188
  • 258
  • 1
    I don't think this is a good idea. What about slices? Negative indexes? The ambiguity of a list with a None in it? –  Aug 29 '12 at 22:41
  • 2
    Possible duplicate of [How to get the nth element of a python list or a default if not available](https://stackoverflow.com/questions/2492087/how-to-get-the-nth-element-of-a-python-list-or-a-default-if-not-available) – Rotareti Mar 01 '18 at 19:54

8 Answers8

83

Try:

try:
    return the_list[i]
except IndexError:
    return None

Or, one liner:

l[i] if i < len(l) else None

Example:

>>> l=list(range(5))
>>> i=6
>>> print(l[i] if i < len(l) else None)
None
>>> i=2
>>> print(l[i] if i < len(l) else None)
2
Thom Wiggers
  • 6,938
  • 1
  • 39
  • 65
the wolf
  • 34,510
  • 13
  • 53
  • 71
  • 10
    This has the unfortunate side-effect of using exceptions as control flow, which his original already doesn't and this isn't much less verbose. – Daniel DiPaolo Aug 29 '12 at 21:00
  • This is indeed much better to do, but it isn't exactly less verbose, is it? – BrtH Aug 29 '12 at 21:00
  • 17
    @DanielDiPaolo: In python, using exceptions this way is encouraged and not frowned upon. – BrtH Aug 29 '12 at 21:02
  • 5
    @BrtH: well theres two schools of thought on that the LBYL (Look before you leap (if statements)) crowd and the EAFP (easier to ask forgiveness then permission (try/excepts)) crowd... but yeah in general using exceptions this way is definitely not frowned upon ... but i think it largely comes down to personal preference – Joran Beasley Aug 29 '12 at 21:09
  • 3
    I doubt exception flow will be as performant as a simple if-else. – julx Mar 20 '14 at 10:32
  • 3
    Except that in Python exception flow _is_ more performant: Exceptions are _very_ light in Python, and only incur their (limited) overhead when an exception occurs. An if statement means a comparison is ran when the code would have executed anyway. Check [this answer](https://stackoverflow.com/questions/2522005/cost-of-exception-handlers-in-python) and [this doc](https://docs.python.org/2/faq/design.html#how-fast-are-exceptions) – Anthony Manning-Franklin Aug 08 '17 at 09:07
  • Note that the second version doesn't work with negative indices – WorldSEnder Oct 30 '17 at 14:18
  • second version do not work if i=0, and every value under len(l) – Joe Cabezas May 09 '18 at 19:20
48

I find list slices good for this:

>>> x = [1, 2, 3]
>>> a = x [1:2]
>>> a
[2]
>>> b = x [4:5]
>>> b
[]

So, always access x[i:i+1], if you want x[i]. You'll get a list with the required element if it exists. Otherwise, you get an empty list.

Hashken
  • 4,396
  • 7
  • 35
  • 51
  • 18
    Addendum: This can be expanded to be `my_list[idx: idx+1] or "Default_value"`. Readable and succinct! :) – Erich Aug 30 '17 at 20:56
  • 1
    This should be the accepted answer as a one-liner that avoids an if and works with empty lists. It works well with lists created by list comprehension as well: `a = [1,3,5,7,9]; b = [num for num in a if num % 2 == 0][0:1] or "No evens found"; print(b) No evens found` – Aaron D Oct 18 '17 at 11:17
  • 1
    @Erich Nice but it results in a list, instead of a value. So full one liner will look ugly: `(my_list[idx: idx+1] or ["Default_value"])[0]` – Sergey Nudnov Nov 05 '21 at 20:54
  • @SergeyNudnov I agree with your solution. It's a bit hairy but the cleanest way to do it as a one-liner. I would say that at this point it might warrant a small helper function in a personal lib – Erich Nov 09 '21 at 07:24
  • You can make this much shorter using unpacking assignment. Given the list splice `x[1:2]`, you could assign it to `var` via many forms of unpacking. Tuple unpacking: `(var,) = x[1:2]` or `var, = x[1:2]` or `var, *_ = x[1:2]`. List unpacking: `[var] = x[1:2]`. Whilst I normally don't use brackets for tuple unpacking, in this case I'd prefer `(var,) = x[1:2]` as it's both short and (IMHO) explicit, since the tuple form should be more recognisable. – daveruinseverything Mar 17 '22 at 09:54
  • @daveruinseverything If we are checking out of index, it would still give an error: `ValueError: not enough values to unpack (expected 1, got 0)`. – Hashken Apr 28 '22 at 14:23
13

If you are dealing with small lists, you do not need to add an if statement or something of the sorts. An easy solution is to transform the list into a dict. Then you can use dict.get:

table = dict(enumerate(the_list))
return table.get(i)

You can even set another default value than None, using the second argument to dict.get. For example, use table.get(i, 'unknown') to return 'unknown' if the index is out of range.

Note that this method does not work with negative indices.

dusk
  • 1,799
  • 18
  • 25
11

Combining slicing and iterating

next(iter(the_list[i:i+1]), None)
  • 2
    Note to readers: The only way I can interpret this answer is that it is a joke. You should never use this as a solution to the problem in OP's question. – Michael Geary May 26 '21 at 06:17
  • 6
    @MichaelGeary: I don't see why you think this is a joke. At the time of writing this is the only answer, which does not refer to the list twice, which is very handy when your list is a temporary object. Aside from that you are able choose the alternative return value all in one statement. – mic Mar 12 '22 at 18:51
  • I like this because of it's one liner aspect but it seems it is actually the least performant. Check out @XerCis's [answer](https://stackoverflow.com/a/68112414/8285811) with benchmarks. – Akaisteph7 Sep 13 '22 at 19:59
9

For your purposes you can exclude the else part as None is return by default if a given condition is not met.

def return_ele(x, i):
    if len(x) > i: return x[i]

Result

>>> x = [2,3,4]
>>> b = return_ele(x, 2)
>>> b
4
>>> b = return_ele(x, 5)
>>> b
>>> type(b)
<type 'NoneType'>
Akavall
  • 82,592
  • 51
  • 207
  • 251
8
return the_list[i] if len(the_list) > i else None
Emmett Butler
  • 5,969
  • 2
  • 29
  • 47
7

1. if...else...

l = [1, 2, 3, 4, 5]
for i, current in enumerate(l):
    following = l[i + 1] if i + 1 < len(l) else None
    print(current, following)
# 1 2
# 2 3
# 3 4
# 4 5
# 5 None

2. try...except...

l = [1, 2, 3, 4, 5]
for i, current in enumerate(l):
    try:
        following = l[i + 1]
    except IndexError:
        following = None
    print(current, following)
# 1 2
# 2 3
# 3 4
# 4 5
# 5 None

3. dict

suitable for small list

l = [1, 2, 3, 4, 5]
dl = dict(enumerate(l))
for i, current in enumerate(l):
    following = dl.get(i + 1)
    print(current, following)
# 1 2
# 2 3
# 3 4
# 4 5
# 5 None

4. List slicing

l = [1, 2, 3, 4, 5]
for i, current in enumerate(l):
    following = next(iter(l[i + 1:i + 2]), None)
    print(current, following)
# 1 2
# 2 3
# 3 4
# 4 5
# 5 None

5. itertools.zip_longest

from itertools import zip_longest

l = [1, 2, 3, 4, 5]
for i, (current, following) in enumerate(zip_longest(l, l[1:])):
    print(current, following)
# 1 2
# 2 3
# 3 4
# 4 5
# 5 None

Using Jupyter magic command of %%timeit

init

from itertools import zip_longest

l = list(range(10000000))

Result

Method Consume
if...else... 2.62 s
try...except... 1.14 s
dict 2.61 s
List slicing 3.75 s
itertools.zip_longest 1.14 s
XerCis
  • 917
  • 7
  • 6
  • 1
    Thank you for presenting several scenarios + benchmarking them. A very insightful answer! – MiKK Dec 20 '21 at 22:29
1

Another one-liner:

return((the_list + [None] * i)[i])
Bogdán
  • 31
  • 4