220

In Python, how can I simply do the equivalent of dictionary.get(key, default) for lists - i.e., how can I simply get the nth element of a list, or a default value if not available?

For example, given a list myList, how can I get 5 if myList is empty, or myList[0] otherwise?

Karl Knechtel
61.2k14 gold badges131 silver badges192 bronze badges
asked Mar 22, 2010 at 12:17

14 Answers 14

176
l[index] if index < len(l) else default

To support negative indices we can use:

l[index] if -len(l) <= index < len(l) else default
John Kugelman
364k70 gold badges554 silver badges599 bronze badges
answered Mar 22, 2010 at 12:18

12 Comments

Doesn't work if your list doesn't have a name, though - for instance, in a list comprehension.
You seem to forgot the case of negative index. Eg. index == -1000000 should return default.
@nodakai, good point. I've sometimes been bitten by this. x[index] if 0 <= index < len(x) else default would be better if index can ever be negative.
@nodakai wow - that is a great example of why it may be better to use try/except, than to try to correctly code the test so it never fails. I still don't like to rely on try/except, for a case that I know will happen, but this increases my willingness to consider it.
@ToolmakerSteve: Your formula for valid_index() is incorrect. Negative indices are legal in Python - it should be -len(l) <= index < len(l).
|
78
try:
 a = b[n]
except IndexError:
 a = default

Edit: I removed the check for TypeError - probably better to let the caller handle this.

answered Mar 22, 2010 at 12:18

1 Comment

I like this. Just wrap it around a function so that you can call it with an iterable as an argument. Easier to read.
55

Just discovered that :

next(iter(myList), 5)

iter(l) returns an iterator on myList, next() consumes the first element of the iterator, and raises a StopIteration error except if called with a default value, which is the case here, the second argument, 5

This only works when you want the 1st element, which is the case in your example, but not in the text of you question, so...

Additionally, it does not need to create temporary lists in memory and it works for any kind of iterable, even if it does not have a name (see Xiong Chiamiov's comment on gruszczy's answer)

serv-inc
38.6k9 gold badges193 silver badges215 bronze badges
answered Apr 4, 2014 at 14:12

4 Comments

Combined with the other answers: next(iter(myList[n:n+1]), 5) Now it works for the nth element.
That would not work with non lists. At this point, try:except-ing an IndexError reads like a better idea IMHO. Also, it creates a list in memory.
The try variant isn't a one-liner (ask asked for by OP). It only works for lists because myList is a list as specified by OP (to be precise, it is something indexable). Creating a copy in memory isn't costly here because I'm creating a list of one single (or none) element here. Sure, a little overhead, but not worth mentioning unless you do that millions of times in a loop. Btw, I guess creating and catching an IndexError exception is probably more costly.
readability counts :) If you're at the point where raising an IndexError has a significant cost, maybe you should be doing the whole thing in Python.
53
(a[n:]+[default])[0]

This is probably better as a gets larger

(a[n:n+1]+[default])[0]

This works because if a[n:] is an empty list if n => len(a)

Here is an example of how this works with range(5)

>>> range(5)[3:4]
[3]
>>> range(5)[4:5]
[4]
>>> range(5)[5:6]
[]
>>> range(5)[6:7]
[]

And the full expression

>>> (range(5)[3:4]+[999])[0]
3
>>> (range(5)[4:5]+[999])[0]
4
>>> (range(5)[5:6]+[999])[0]
999
>>> (range(5)[6:7]+[999])[0]
999
answered Mar 22, 2010 at 12:25

6 Comments

Would you write this in actual code without a comment to explain it?
@Peter Hansen, only if I was golfing ;) However it does work on all versions of Python. The Accepted answer only works on 2.5+
It invloves creating 3 temporary lists and accessing 2 indices in order to select an item, though.
While I'd never do this in "real" code, it's a nice concise one-liner that I can easily use in a python REPL, so you get my upvote.
next(iter(lst[i:i+1]), default) -- just another entry in the cryptic ugly one-liners competition.
|
33
(L[n:n+1] or [somedefault])[0]
answered Mar 22, 2010 at 12:19

7 Comments

+1 because this made me go learn what [] or ... did. However, I would personally just use the accepted solution, as it reads easily (for novices). Granted, wrapping it in a 'def' with comment would make that largely a non-issue.
What if L[n] == False or L[n] == None or L[n] == [] or more globally anything that evaluates to False ?
@JoachimJablon: Still works. The slice returns a list, and [False] is true.
Oh, didn't realised the list was checked and not the value, indeed.
Why do you wrap this in a tuple? I think myval = l[n:n+1] or [somedefault] would work just fine?
|
17

... looking for an equivalent in python of dict.get(key, default) for lists

There is an itertools recipes that does this for general iterables. For convenience, you can > pip install more_itertools and import this third-party library that implements such recipes for you:

Code

import more_itertools as mit
mit.nth([1, 2, 3], 0)
# 1 
mit.nth([], 0, 5)
# 5 

Detail

Here is the implementation of the nth recipe:

def nth(iterable, n, default=None):
 "Returns the nth item or a default value"
 return next(itertools.islice(iterable, n, None), default)

Like dict.get(), this tool returns a default for missing indices. It applies to general iterables:

mit.nth((0, 1, 2), 1) # tuple
# 1
mit.nth(range(3), 1) # range generator (py3)
# 1
mit.nth(iter([0, 1, 2]), 1) # list iterator 
# 1 
answered Aug 26, 2017 at 19:05

Comments

5

Using Python 3.4's contextlib.suppress(exceptions) to build a getitem() method similar to getattr().

import contextlib
def getitem(iterable, index, default=None):
 """Return iterable[index] or default if IndexError is raised."""
 with contextlib.suppress(IndexError):
 return iterable[index]
 return default
answered Aug 31, 2018 at 15:21

Comments

4

A cheap solution is to really make a dict with enumerate and use .get() as usual, like

 dict(enumerate(l)).get(7, my_default)
answered Nov 7, 2019 at 9:21

2 Comments

This is very slow to run because whole list is converted to a dictionary only to get later just one element.
Very nice one; definitely works for my use case; thank you!
4

With unpacking:

b, = a[n:n+1] or [default]
answered Oct 11, 2022 at 19:35

Comments

2

After reading through the answers, I'm going to use:

(L[n:] or [somedefault])[0]
answered Sep 21, 2020 at 15:14

3 Comments

Probably you want (L[n:n+1] or [somedefault])[0] (n+1 added) instead, because L[n:] creates long list and copies all elements from n to the end. Only to get just one element afterwards. While L[n:n+1] creates a list of just 1 element at most.
I believe I chose the form I give because I need to compute n, and I do not want to compute it twice, nor introduce a variable. My lists are short. -- Also, the answer L[n:n+1] has already been provided by Ignacio Vazquez-Abrams
I don't get it. If you need to compute n, you will have it in the context, there's no need to compute it twice. Why not sum one and prevent the rest of the list from being copied?
2

Combining @Joachim's with the above, you could use

next(iter(my_list[index:index+1]), default)

Examples:

next(iter(range(10)[8:9]), 11)
8
>>> next(iter(range(10)[12:13]), 11)
11

Or, maybe more clear, but without the len

my_list[index] if my_list[index:index + 1] else default
answered Apr 27, 2018 at 8:04

1 Comment

if my_list is a very large list, this could behave badly. Maybe next(iter(my_list[index:index + 1]), default)
2

Althought this is not a one-liner solution, you can define a function with a default value like so:

def get_val(myList, idx, default=5):
 try:
 return myList[idx]
 except IndexError:
 return default
answered Jul 4, 2021 at 21:55

2 Comments

Thanks @VictorSchröder. Edited the answer!
I removed my previous comment, as the issue was fixed. The solution still has limitations in case myList is not of List type or if idx is not an int, but that's out of the scope of this question.
0

For a small index, such as when parsing up to k arguments, I'd build a new list of length k, with added elements set to d, as follows:

def fill(l, k, d): 
 return l + (k - len(l)) * [d]

Typical usage:

N = 2
arg_one, arg_two = fill("first_word and the rest".split(maxsplit=N - 1), N, None)
# arg_one == "first_word"
# arg_two == "and the rest"

Same example, with a short list:

arg_one, arg_two = fill("one_word_only".split(maxsplit=N - 1), N, None)
# arg_one == "one_word_only"
# arg_two == None
answered Aug 11, 2022 at 17:43

Comments

0

I like it more inline, therefore I created a little streams-like library (https://pypi.org/project/tinystream/).

my_list = ["a", "b", "c"]
stream = Stream(my_list)
assert stream[4].absent
assert stream[0].get() == "a"

Since the index element is of type Opt, you can also do more stuff like:

assert stream[4].get('X') == 'X' # default if absent
assert stream[0].map(str.upper).filter(lambda x: x == 'A').present
answered Dec 17, 2023 at 10:19

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.