'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 matching the tuple (1,5), the same as d[1,5].
d{1,5} isn't even valid Python (as far as I can tell ...), though it might be handy: The curly braces suggest an unordered set or a dictionary, so returning a dictionary containing the specified keys would look very plausible to me.
d[{1,5}] would also make sense ("here's a set of keys, give me the matching items"), and {1, 5} is an unhashable set, so there can't be a key that matches it -- but of course it throws an error, too.
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)
Solution 1:[1]
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.
Solution 2:[2]
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}
Solution 3:[3]
To slice a dictionary, Convert it to a list of tuples using d.items(), slice the list and create a dictionary out of it.
Here.
d = {1:2, 3:4, 5:6, 7:8}
To get the first 2 items
first_two = dict(list(d.items())[:2])
first_two
{1: 2, 3: 4}
Solution 4:[4]
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
Solution 5:[5]
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}
Solution 6:[6]
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}
Solution 7:[7]
Another option is to convert the dictionary into a pandas Series object and then locating the specified indexes:
>>> d = {1:2, 3:4, 5:6, 7:8}
>>> l = [1,5]
>>> import pandas as pd
>>> pd.Series(d).loc[l].to_dict()
{1: 2, 5: 6}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jpp |
| Solution 2 | |
| Solution 3 | sostom |
| Solution 4 | |
| Solution 5 | itzMEonTV |
| Solution 6 | diman Bond |
| Solution 7 | Ivan De Paz Centeno |
