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?
14 Answers 14
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
12 Comments
index == -1000000
should return default
.x[index] if 0 <= index < len(x) else default
would be better if index
can ever be negative.valid_index()
is incorrect. Negative indices are legal in Python - it should be -len(l) <= index < len(l)
.try:
a = b[n]
except IndexError:
a = default
Edit: I removed the check for TypeError - probably better to let the caller handle this.
1 Comment
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)
4 Comments
next(iter(myList[n:n+1]), 5)
Now it works for the n
th element.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.(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
6 Comments
next(iter(lst[i:i+1]), default)
-- just another entry in the cryptic ugly one-liners competition.(L[n:n+1] or [somedefault])[0]
7 Comments
[] 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.L[n] == False
or L[n] == None
or L[n] == []
or more globally anything that evaluates to False ?[False]
is true.myval = l[n:n+1] or [somedefault]
would work just fine?... 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
Comments
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
Comments
A cheap solution is to really make a dict with enumerate and use .get()
as usual, like
dict(enumerate(l)).get(7, my_default)
With unpacking:
b, = a[n:n+1] or [default]
Comments
After reading through the answers, I'm going to use:
(L[n:] or [somedefault])[0]
3 Comments
(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.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-Abramsn
, 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?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
1 Comment
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
2 Comments
myList
is not of List
type or if idx
is not an int
, but that's out of the scope of this question.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
Comments
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