147

I have a dictionary in Python, and what I want to do is get some values from it as a list, but I don't know if this is supported by the implementation.

myDictionary.get('firstKey')   # works fine

myDictionary.get('firstKey','secondKey')
# gives me a KeyError -> OK, get is not defined for multiple keys
myDictionary['firstKey','secondKey']   # doesn't work either

Is there any way I can achieve this? In my example it looks easy, but let's say I have a dictionary of 20 entries, and I want to get 5 keys. Is there any other way than doing the following?

myDictionary.get('firstKey')
myDictionary.get('secondKey')
myDictionary.get('thirdKey')
myDictionary.get('fourthKey')
myDictionary.get('fifthKey')
wovano
  • 4,543
  • 5
  • 22
  • 49
PKlumpp
  • 4,913
  • 8
  • 36
  • 64
  • 1
    Possible duplicate of [Python dictionary: Get list of values for list of keys](https://stackoverflow.com/questions/18453566/python-dictionary-get-list-of-values-for-list-of-keys) – Jundiaius May 08 '18 at 12:56
  • 1
    Super hard question to search for. Glad I finally found this! – Stephen Sep 16 '19 at 15:09

13 Answers13

174

There already exists a function for this:

from operator import itemgetter

my_dict = {x: x**2 for x in range(10)}

itemgetter(1, 3, 2, 5)(my_dict)
#>>> (1, 9, 4, 25)

itemgetter will return a tuple if more than one argument is passed. To pass a list to itemgetter, use

itemgetter(*wanted_keys)(my_dict)

Keep in mind that itemgetter does not wrap its output in a tuple when only one key is requested, and does not support zero keys being requested.

Veedrac
  • 58,273
  • 15
  • 112
  • 169
  • this is cool. it's not as short as I expected, but nice to know ;) – PKlumpp Jun 13 '14 at 12:05
  • 3
    How much shorter can you get? If the name is too long... `g = itemgetter`. o.O – Veedrac Jun 13 '14 at 12:10
  • I expected it to have some built in syntax that I couldn't find. of course you can make it shorter your way, but in any code review I would be asked: wtf is g(mykeys)(myDict) :D – PKlumpp Jun 13 '14 at 12:16
  • 2
    Is there an option to return a dictionary of key: value pairs for specified list of keys rather than just the values? – alancalvitti Dec 26 '18 at 19:57
  • @alancalvitti I don't think so. – Veedrac Dec 26 '18 at 20:40
  • 2
    @alancalvitti you can always do `{key: mydictionary.get(key) for key in keys}` – Buggy Oct 17 '19 at 12:59
  • 3
    Small catch with this, `itemgetter` requires at least one argument, so if you were looking to do something like `itemgetter(*some_keys)(some_dict)`, it will fail if `some_keys` is an empty list. – Christian Reall-Fluharty Nov 16 '19 at 00:42
  • 2
    itemgetter will not deal with KeyError – NeilG Feb 11 '20 at 02:58
  • If you want to extract the values into multiple variables and not to a tuple you can mix @Veedrac and @Epion solutions: `first_val, second_val = itemgetter(1, 3)(my_dict)` – ET-CS Jul 21 '20 at 13:50
  • Awesome solution, but definitely a case for a new language feature: one that could take an indefinite number of values 0-n, always returns some type of Sequence, allows for specific fields, and reliably returns the same type. – Joe Sadoski Oct 04 '21 at 14:30
107

Use a for loop:

keys = ['firstKey', 'secondKey', 'thirdKey']
for key in keys:
    myDictionary.get(key)

or a list comprehension:

[myDictionary.get(key) for key in keys]
ComputerFellow
  • 11,710
  • 12
  • 50
  • 61
  • 3
    ok I already thought about this, but is there really no implemented way? I mean, this is not something completely odd I guess – PKlumpp Jun 13 '14 at 11:21
  • 4
    If it is guaranteed that every key in keys is in the dictionary, you can write `[myDictionary[key] for key in keys]`, but it does not get much simpler than that. – timgeb Jun 13 '14 at 11:34
  • 4
    or `[my_dict.get(x) for x in ['firstKey', 'secondKey', 'thirdKey']]`for a single line solution but that is as compact as you will get. – Padraic Cunningham Jun 13 '14 at 11:34
  • Depending on the use-case you might want an `if` statement to check for `None` values returned by the `get` method: `[mydict.get(key) for key in keys if key in mydict]` – Carolyn Conway Feb 08 '17 at 22:54
  • 1
    @timgeb comment is True, and it is faster. – Muhammad Yasirroni Mar 17 '22 at 08:58
62

I'd suggest the very useful map function, which allows a function to operate element-wise on a list:

mydictionary = {'a': 'apple', 'b': 'bear', 'c': 'castle'}
keys = ['b', 'c']

values = list( map(mydictionary.get, keys) )

# values = ['bear', 'castle']
scottt
  • 809
  • 6
  • 8
  • 3
    One amendment to get this to work, you have to encase `map` with a list: `values = list( map(mydictionary.get, keys) )` – kilozulu Jan 28 '20 at 21:19
  • And this _does_ deal with KeyError – NeilG Feb 11 '20 at 03:04
  • 1
    I timed this solution compared to a list comprehension (which would have otherwise been my preference) and this solution without encasing in a list is 3.5x faster so (+1) from me for the cases when we can use the iterator directly. (Encasing in a list makes it take just as long and for that case I would just do the list comprehension which seems easier to read). – Dan Boschen Jan 23 '22 at 18:17
  • Nice solution unless you care to be more strict, given that the .get will return None if key is not found. One might wants it to crash instead – George Pligoropoulos Nov 05 '22 at 20:11
  • This dealing with the `None` and being robust to passing a single key makes it a great solution. Wrapping in list lets you insert the output into a numpy array which will transform the `None` into a `np.nan`. – Heymans Jun 06 '23 at 22:02
11

You can use At from pydash:

from pydash import at
my_dict = {'a': 1, 'b': 2, 'c': 3}
my_list = at(my_dict, 'a', 'b')
my_list == [1, 2]
bustawin
  • 684
  • 7
  • 11
9

As I see no similar answer here - it is worth pointing out that with the usage of a (list / generator) comprehension, you can unpack those multiple values and assign them to multiple variables in a single line of code:

first_val, second_val = (myDict.get(key) for key in [first_key, second_key])
Epion
  • 458
  • 3
  • 7
8

I think list comprehension is one of the cleanest ways that doesn't need any additional imports:

>>> d={"foo": 1, "bar": 2, "baz": 3}
>>> a = [d.get(k) for k in ["foo", "bar", "baz"]]
>>> a
[1, 2, 3]

Or if you want the values as individual variables then use multiple-assignment:

>>> a,b,c = [d.get(k) for k in ["foo", "bar", "baz"]]
>>> a,b,c
(1, 2, 3)
Andy Brown
  • 11,766
  • 2
  • 42
  • 61
7

%timeit response of all the answers listed above. My apologies if missed some of the solutions, and I used my judgment to club similar answers. itemgetter seems to be the winner to me. pydash reports much lesser time but I don't know why it ran lesser loops and don't know if I can call it a fastest. Your thoughts?

from operator import itemgetter

my_dict = {x: x**2 for x in range(10)}
req_keys = [1, 3, 2, 5]
%timeit itemgetter(1, 3, 2, 5)(my_dict)
257 ns ± 4.61 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit [my_dict.get(key) for key in req_keys]
604 ns ± 6.94 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)


%timeit list( map(my_dict.get, req_keys) )
529 ns ± 34.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)


!pip install pydash
from pydash import at

%timeit at(my_dict, 1, 3, 2, 5)
22.2 µs ± 572 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)


%timeit (my_dict.get(key) for key in req_keys)
308 ns ± 6.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

s = pd.Series(my_dict)

%timeit s[req_keys]
334 µs ± 58.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Mainland
  • 4,110
  • 3
  • 25
  • 56
  • just want to point out that pydash is actually much slower at `22200 ns` for `at(my_dict, 1, 3, 2, 5)` and `334000 ns` for `s[req_keys]` due to the fact the measurement is microseconds, not nanoseconds – Tcll May 17 '23 at 13:03
5

If you have pandas installed you can turn it into a series with the keys as the index. So something like

import pandas as pd

s = pd.Series(my_dict)

s[['key1', 'key3', 'key2']]
Mosqueteiro
  • 124
  • 1
  • 4
1
def get_all_values(nested_dictionary):
    for key, value in nested_dictionary.items():
        if type(value) is dict:
            get_all_values(value)
        else:
            print(key, ":", value)

nested_dictionary = {'ResponseCode': 200, 'Data': {'256': {'StartDate': '2022-02-07', 'EndDate': '2022-02-27', 'IsStoreClose': False, 'StoreTypeMsg': 'Manual Processing Stopped', 'is_sync': False}}}

get_all_values(nested_dictionary)
1

If you want to retain the mapping of the values to the keys, you should use a dict comprehension instead:

{key: myDictionary[key] for key in [
  'firstKey',
  'secondKey',
  'thirdKey',
  'fourthKey',
  'fifthKey'
]}
tschlich
  • 43
  • 1
  • 5
0

Slighlty different variation of list comprehension approach.

#doc
[dict[key] for key in (tuple_of_searched_keys)]

#example
my_dict = {x: x**2 for x in range(10)}
print([my_dict[key] for key in (8,9)])
marcin2x4
  • 1,321
  • 2
  • 18
  • 44
-1

If the fallback keys are not too many you can do something like this

value = my_dict.get('first_key') or my_dict.get('second_key')
Mux
  • 29
  • 1
  • 3
-2
def get_all_values(nested_dictionary):
    for key, val in nested_dictionary.items():
        data_list = []
        if type(val) is dict:
            for key1, val1 in val.items():
                data_list.append(val1)

    return data_list