在Python中,如何根据键/值过滤字典并创建子字典?

5

好的,我被卡住了,需要从这里得到一些帮助...

如果我有一个像这样的主字典:

data = [ {"key1": "value1", "key2": "value2", "key1": "value3"},  
{"key1": "value4", "key2": "value5", "key1": "value6"}, 
{"key1": "value1", "key2": "value8", "key1": "value9"} ]

现在,我需要浏览那个字典并格式化一些数据,例如:
for datadict in data:  
    for key, value in datadict.items():  
    ...filter the data...

现在,在同一个循环中我如何(如果可能的话...如果不行,请建议替代方案)检查某些键的值,如果这些值与我的预设匹配,则将整个列表添加到另一个字典中,从而根据特定键和值从主字典中逐步创建较小的字典?

假设我想创建一个子字典,其中 key1 的值为 "value1" 的所有列表,对于上面的列表我将得到以下结果:

subdata = [ {"key1": "value1", "key2": "value2", "key1": "value3"},  
{"key1": "value1", "key2": "value8", "key1": "value9"} ]

1
“像这样的主要字典”是不正确的。你有一个字典列表。 - S.Lott
字典键是唯一的,因此您无法使用重复键构造字典,例如:{"key1": "value1", "key2": "value2", "key1": "value3"}; 结果为{'key2': 'value2', 'key1': 'value3'}。如果确实需要重复项,则需要使用列表,因此整体结构将是列表的列表或字典值应为元组或列表。 - Ned Deily
5个回答

9
这里有一个不太美观的方法。结果是一个生成器,但如果你真的想要一个列表,可以用list()来包围它。大多数情况下这并不重要。
谓词是一个函数,它为每个键值对决定字典列表是否适合。默认情况下接受所有的键值对。如果字典中没有匹配的k/v对,则被拒绝。
def filter_data(data, predicate=lambda k, v: True):
    for d in data:
         for k, v in d.items():
               if predicate(k, v):
                    yield d


test_data = [{"key1":"value1", "key2":"value2"}, {"key1":"blabla"}, {"key1":"value1", "eh":"uh"}]
list(filter_data(test_data, lambda k, v: k == "key1" and v == "value1"))
# [{'key2': 'value2', 'key1': 'value1'}, {'key1': 'value1', 'eh': 'uh'}]

2
“不太好看”?不同意。这非常漂亮。 - S.Lott
谢谢:)我倾向于认为像那样的阶梯函数很丑陋。 - Skurmedel
2
@Skurmedel:你的函数写得非常优雅,很容易看出它是如何通过简单的步骤完成工作的;这样可以避免读者在脑海中解析复杂的一行代码。 - John Machin
哇,那就是我正在寻找的东西......并且我不同意“不太好看”的评论。 - Crazy Serb

2

除了其他评论和答案指出的问题(例如,字典中不能有多个相同的键等等),下面是我的解决方案:

def select_sublist(list_of_dicts, **kwargs):
    return [d for d in list_of_dicts 
            if all(d.get(k)==kwargs[k] for k in kwargs)]

subdata = select_sublist(data, key1='value1')

1

这是一个古老的问题,但出于某种原因没有单行语法的答案:

{ k: v for k, v in <SOURCE_DICTIONARY>.iteritems() if <CONDITION> }

例如:

src_dict = { 1: 'a', 2: 'b', 3: 'c', 4: 'd' }
predicate = lambda k, v: k % 2 == 0
filtered_dict = { k: v for k, v in src_dict.iteritems() if predicate(k, v) }

print "Source dictionary:", src_dict
print "Filtered dictionary:", filtered_dict

将产生以下输出:
Source dictionary: {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
Filtered dictionary: {2: 'b', 4: 'd'}

1
答案太简单了,我猜我们缺少一些信息。无论如何:
result = []
for datadict in data:
    for key, value in datadict.items():
        thefiltering()

    if datadict.get('matchkey') == 'matchvalue':
        result.append(datadict)

另外,你的“主要字典”不是一个字典,而是一个列表。只是想澄清一下。

0
受 Skurmedal 回答的启发,我将其拆分为递归方案,以处理嵌套字典数据库。在这种情况下,“记录”是树干处的子字典。谓词定义了我们要寻找哪些记录 - 那些匹配某个(键,值)对的记录,其中这些对可能深度嵌套。
def filter_dict(the_dict, predicate=lambda k, v: True):
    for k, v in the_dict.iteritems():
        if isinstance(v, dict) and _filter_dict_sub(predicate, v):
            yield k, v

def _filter_dict_sub(predicate, the_dict):
    for k, v in the_dict.iteritems():
        if isinstance(v, dict) and filter_dict_sub(predicate, v):
            return True
        if predicate(k, v):
            return True
    return False

由于这是一个生成器,您可能需要使用dict(filter_dict(the_dict))进行包装,以获取过滤后的字典。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接