过滤字典仅包含某些键?


496

我有一个dict包含大量条目的条目。我只对其中一些感兴趣。有没有一种简单的方法可以将其他所有元素都修剪掉?


最好说出哪种类型的键(整数,字符串,日期,任意对象?),从而确定是否有简单的(字符串,正则表达式,列表成员或数字不等式)测试可以检查哪些键在输入或输出中。否则,我们需要调用任意函数来确定这一点。
smci

@smci字符串键。甚至我都没有想到我可以使用其他任何东西。我已经使用JS和PHP进行编码了很长时间……
mpen

Answers:


654

构建一个新的字典:

dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }

使用字典理解。

如果使用缺少它们的版本(例如Python 2.6和更早版本),请使其成为dict((your_key, old_dict[your_key]) for ...)。一样,尽管丑陋。

请注意,这与jnnnnn的版本不同,对于old_dict任何大小的,都具有稳定的性能(仅取决于your_keys的数量)。在速度和内存方面。由于这是一个生成器表达式,因此它一次只能处理一项,并且不会浏览old_dict的所有项。

就地删除所有内容:

unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]

8
“使用字典理解,如果您使用缺少它们的版本” ==版本<= 2.6
getekha 2011年

8
如果old_dict中不存在文件管理器密钥之一,则抛出KeyError。我建议{k:d [k]过滤器中的k如果d中的k}
彼得·吉布森

1
@PeterGibson是的,如果这是要求的一部分,则需要对此做些事情。是否以静默方式删除键,添加默认值或其他方式取决于您在做什么。在很多用例中,您的方法是错误的。在很多地方,如果缺少某个键,则old_dict表示其他地方存在错误,在那种情况下,我非常喜欢错误而不是默默地错误的结果。

@delnan,如果d大,“ if k in d”的添加也会使您减速,我只是认为这值得一提
Peter Gibson 2012年

7
@PeterGibson并非如此,字典查找为O(1)。

130

dict理解稍微更优雅:

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}

已投票。我在考虑添加与此类似的答案。只是出于好奇,为什么{k:v表示dict.items()中的k,v,而不是{k:dict [k]表示dict中的k ...}是否存在性能差异?
哈特·西马

4
回答了我自己的问题。{dict中的k的{k:dict [k] ...}大约快20-25%,至少在Python 2.7.6中,具有26个项的字典(timeit(...,setup =“ d = {chr(x + 97):x + 1 for range in 26(26)}“)),具体取决于要滤除的项目数量(滤除辅音键比滤除元音键要快,因为您正在查找较少的项目)。随着字典大小的增加,性能上的差异可能会变得不那么重要。
哈特·西马

5
如果mydict.iteritems()改用,则可能是相同的性能。.items()创建另一个列表。
专利

64

这是python 2.6中的示例:

>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}

过滤部分是if语句。

如果您只想选择很多键中的几个键,则此方法比delnan的答案要慢。


11
除了我可能会用if key in ('x','y','z')
mpen 2010年

如果您已经知道要使用哪些键,请使用delnan的答案。如果您需要使用if语句测试每个键,请使用ransford的答案。
jnnnnn

1
该解决方案还有一个优势。如果字典是从昂贵的函数调用返回的(即a / old_dict是函数调用),则此解决方案仅调用一次函数。在命令式环境中,将函数返回的字典存储在变量中并不重要,但是在功能性环境(例如lambda)中,这是关键的观察。
gae123 '16


20

代码1:

dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
    if key % 2 == 0:
        d1[key] = value

代码2:

dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}

代码3:

dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}

使用number = 1000随时间测量所有代码性能,并为每个代码收集1000次。

在此处输入图片说明

对于python 3.6,三种过滤器dict键的性能几乎相同。对于python 2.7,代码3稍快一些。


只是好奇,您是用Python绘制的图吗?
user5359531 17-10-19

1
R中的ggplot2
tidyverse的

18

这一个线性lambda应该可以工作:

dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])

这是一个例子:

my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")

# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}

这是对列表键(i在x中)进行迭代的基本列表理解,如果键位于所需的键列表(y)中,则输出元组(键,值)对的列表。dict()将整个内容包装为dict对象。


应该使用setfor wanted_keys,但是看起来不错。
mpen

如果我的原始字典包含值列表而不是列表,这将给我一个空白的字典。任何解决方法?
FaCoffee,2015年

@Francesco,您能举个例子吗?如果我运行:dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z')),它将{'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}按预期返回。
2015年

我用:dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}进行了尝试,结果是{},我认为这是空白字典。
FaCoffee 2015年

一件事,“字典”是保留字,因此您不应使用它来命名字典。您要拔出的钥匙是什么?如果我运行:foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2')),我得到:{'0': [1, 3], '2': [1, 4]}这是预期的结果
吉姆(Jim)

14

给定您的原始字典orig和您感兴趣的条目集keys

filtered = dict(zip(keys, [orig[k] for k in keys]))

这不如delnan的答案那么好,但是应该可以在每个感兴趣的Python版本中使用。但是,它对于keys原始字典中存在的每个元素都是脆弱的。


好吧,这基本上是我的dict理解的“元组生成器版本”的急切版本。确实非常兼容,尽管生成器表达式是在2005年春季的2.4中引入的-认真地讲,有人还在使用吗?

1
我不同意。2.3确实不应该再存在了。然而,随着2.3使用的过时的调查结果显示:moinmo.in/PollAboutRequiringPython24短版:RHEL4,SLES9,随OS X 10.4

7

基于delnan接受的答案。

如果您想要的键之一不在old_dict中怎么办?delnan解决方案将引发您可以捕获的KeyError异常。如果那不是您所需要的,也许您想:

  1. 仅在old_dict和您的通缉钥匙组中包含存在的钥匙。

    old_dict = {'name':"Foobar", 'baz':42}
    wanted_keys = ['name', 'age']
    new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys())}
    
    >>> new_dict
    {'name': 'Foobar'}
  2. 具有在old_dict中未设置的键的默认值。

    default = None
    new_dict = {k: old_dict[k] if k in old_dict else default for k in wanted_keys}
    
    >>> new_dict
    {'age': None, 'name': 'Foobar'}

您也可以这样做{k: old_dict.get(k, default) for k in ...}
Moberg

6

此功能可以解决问题:

def include_keys(dictionary, keys):
    """Filters a dict by only including certain keys."""
    key_set = set(keys) & set(dictionary.keys())
    return {key: dictionary[key] for key in key_set}

就像delnan的版本一样,此版本使用字典理解,并且对于大型字典具有稳定的性能(仅取决于您允许的键数,而不取决于字典中键的总数)。

就像MyGGan的版本一样,此键允许您的键列表包含字典中可能不存在的键。

另外,这是相反的,您可以在其中通过排除原稿中的某些键来创建字典:

def exclude_keys(dictionary, keys):
    """Filters a dict by excluding certain keys."""
    key_set = set(dictionary.keys()) - set(keys)
    return {key: dictionary[key] for key in key_set}

请注意,与delnan版本不同,该操作未在适当位置完成,因此性能与字典中键的数量有关。但是,这样做的好处是该函数不会修改提供的字典。

编辑:添加了一个单独的功能,用于从字典中排除某些键。


您应该允许keys任何形式的迭代,例如set接受的迭代。
mpen

啊,打个好电话,谢谢你指出这一点。我将进行更新。
瑞安

我想知道您是否拥有两个功能更好。如果你问10个人“并不invert意味着keys参数保持,或者说keys参数被拒绝?”,多少人会同意吗?
溜冰

更新。让我知道你的想法。
瑞安

如果输入字典使用列表代替值,则这似乎不起作用。在这种情况下,您将获得无效的裁决。任何解决方法?
FaCoffee,2015年

4

如果我们要删除选定的键来制作新字典,可以利用字典理解功能
,例如:

d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}

整齐。仅适用于
Python3。Python2

为Python 2添加了set(d.keys())。当我运行时,它可以正常工作。
Srivastava

2

另外一个选项:

content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())

但是您得到的是list(Python 2)或迭代器(Python 3)filter(),而不是返回dict


filtered起来dict,您就会得到字典!
CMCDragonkai

1

简写:

[s.pop(k) for k in list(s.keys()) if k not in keep]

正如大多数答案所暗示的那样,为了保持简洁,我们必须创建一个重复的对象a list或a dict。这会产生一个一次性的东西,list但会删除original中的键dict


0

这是del在一个衬管中使用的另一种简单方法:

for key in e_keys: del your_dict[key]

e_keys是要排除的键的列表。它会更新您的词典,而不是给您一个新的词典。

如果需要新的输出字典,请在删除之前复制该字典:

new_dict = your_dict.copy()           #Making copy of dict

for key in e_keys: del new_dict[key]

0

您可以使用python-benedict,它是dict的子类。

安装: pip install python-benedict

from benedict import benedict

dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])

它在GitHub上是开源的:https : //github.com/fabiocaccamo/python-benedict


免责声明:我是这个图书馆的作者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.