[python] Slicing a dictionary

I have a dictionary, and would like to pass a part of it to a function, that part being given by a list (or tuple) of keys. Like so:

# the dictionary
d = {1:2, 3:4, 5:6, 7:8}

# the subset of keys I'm interested in
l = (1,5)

Now, ideally I'd like to be able to do this:

>>> d[l]
{1:2, 5:6}

... but that's not working, since it will look for a key named (1,5). And d[1,5] isn't even valid Python (though it seems it would be handy).

I know I can do this:

>>> dict([(key, value) for key,value in d.iteritems() if key in l])
{1: 2, 5: 6}

or this:

>>> dict([(key, d[key]) for key in l])

which is more compact ... but I feel there must be a "better" way of doing this. Am I missing a more elegant solution?

(I'm using Python 2.7)

This question is related to python dictionary

The answer is


set intersection and dict comprehension can be used here

# the dictionary
d = {1:2, 3:4, 5:6, 7:8}

# the subset of keys I'm interested in
l = (1,5)

>>>{key:d[key] for key in set(l) & set(d)}
{1: 2, 5: 6}

Write a dict subclass that accepts a list of keys as an "item" and returns a "slice" of the dictionary:

class SliceableDict(dict):
    default = None
    def __getitem__(self, key):
        if isinstance(key, list):   # use one return statement below
            # uses default value if a key does not exist
            return {k: self.get(k, self.default) for k in key}
            # raises KeyError if a key does not exist
            return {k: self[k] for k in key}
            # omits key if it does not exist
            return {k: self[k] for k in key if k in self}
        return dict.get(self, key)

Usage:

d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d[[1, 5]]   # {1: 2, 5: 6}

Or if you want to use a separate method for this type of access, you can use * to accept any number of arguments:

class SliceableDict(dict):
    def slice(self, *keys):
        return {k: self[k] for k in keys}
        # or one of the others from the first example

d = SliceableDict({1:2, 3:4, 5:6, 7:8})
d.slice(1, 5)     # {1: 2, 5: 6}
keys = 1, 5
d.slice(*keys)    # same

On Python 3 you can use the itertools islice to slice the dict.items() iterator

import itertools

d = {1: 2, 3: 4, 5: 6}

dict(itertools.islice(d.items(), 2))

{1: 2, 3: 4}

Note: this solution does not take into account specific keys. It slices by internal ordering of d, which in Python 3.7+ is guaranteed to be insertion-ordered.


the dictionary

d = {1:2, 3:4, 5:6, 7:8}

the subset of keys I'm interested in

l = (1,5)

answer

{key: d[key] for key in l}

Use a set to intersect on the dict.viewkeys() dictionary view:

l = {1, 5}
{key: d[key] for key in d.viewkeys() & l}

This is Python 2 syntax, in Python 3 use d.keys().

This still uses a loop, but at least the dictionary comprehension is a lot more readable. Using set intersections is very efficient, even if d or l is large.

Demo:

>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = {1, 5}
>>> {key: d[key] for key in d.viewkeys() & l}
{1: 2, 5: 6}