按字典中的值过滤项目

filter items by value in dict

我的字典设置如下:

deck = [{
         'name': 'drew',
         'lvl': 23,
         'items': ['sword', 'axe', 'mana_potion']},
        {
         'name': 'john',
         'lvl': 23,
         'items': ['sword', 'mace', 'health_potion']}]

这是一个基本示例,我需要一种方法来过滤(仅复制{characters})匹配特定值的字符,例如我只想要 23 级的字符,或者携带一把剑。

我正在考虑做这样的事情:

filtered = filter_deck(deck, 'mace')

def filter_deck(self, deck, filt):
        return [{k:v for (k,v) in deck.items() if filt in k}]

和return:

filtered = [{
             'name': 'john',
             'lvl': 23,
             'items': ['sword', 'mace', 'health_potion']}]

我不确定如何过滤特定项目,例如 k:v 或 k:[v1,v2,v3],因为我不知道它是单个值还是值列表,或者如何过滤多个值。

我不确定如何使用多个键来过滤字符。假设我想整理出 23 级的角色,或者有物品 ['sword'] 或物品 ['mace'] 的角色。我将如何以 filter_cards(deck, ['lvl'=23, 'items'=['sword','mace'])

的方式对其进行排序

所以如果任何角色是 23 级,或者携带狼牙棒或剑,他们就会在该列表中。

您的 deck 是一个列表(字典),它没有 .items()。所以尝试这样做 - deck.items() 会失败。

还有语法 -

filter_cards(deck, ['lvl'=23, 'items'=['sword','mace'])

无效,你应该使用字典作为第二个元素。示例 -

filter_cards(deck, {'lvl':23, 'items':['sword','mace']})

您应该使用 filter() 内置函数,如果字典包含其中一个值,则函数 returns 为真。示例 -

def filter_func(dic, filterdic):
    for k,v in filterdic.items():
        if k == 'items':
            if any(elemv in dic[k] for elemv in v):
                return True
        elif v == dic[k]:
            return True
    return False

def filter_cards(deck, filterdic):
    return list(filter(lambda dic, filterdic=filterdic: filter_func(dic, filterdic) , deck))

演示 -

>>> deck = [{
...          'name': 'drew',
...          'lvl': 23,
...          'items': ['sword', 'axe', 'mana_potion']},{
...          'name': 'john',
...          'lvl': 23,
...          'items': ['sword', 'mace', 'health_potion']},{
...          'name': 'somethingelse',
...          'lvl': 10,
...          'items': ['health_potion']}]
>>>
>>>
>>> filter_cards(deck, {'lvl':23, 'items':['sword','mace']})
[{'lvl': 23, 'items': ['sword', 'axe', 'mana_potion'], 'name': 'drew'}, {'lvl': 23, 'items': ['sword', 'mace', 'health_potion'], 'name': 'john'}]

您可以只使用标准 filter 并传入过滤函数,例如:

filter(lambda x: 'mace' in x['items'], deck)
filter(lambda x: x['lvl'] == 23 or any(i in x['items'] for i in ['sword', 'mace']), deck)

等这些 return 个过滤器生成器,所以如果你想将它们打印出来,请将它们变成一个列表:

>>> list(filter(lambda x: x['lvl'] == 23 or any(i in x['items'] for i in ['sword', 'mace']), deck))
[{'items': ['sword', 'axe', 'mana_potion'], 'lvl': 23, 'name': 'drew'},
 {'items': ['sword', 'mace', 'health_potion'], 'lvl': 23, 'name': 'john'}]

您也可以将 lambda 分解为标准函数:

>>> def lvl_sword_mace(x):
...     return x['lvl'] == 23 or any(i in x['items'] for i in ['sword', 'mace'])
...
>>> list(filter(lvl_sword_mace, deck))
[{'items': ['sword', 'axe', 'mana_potion'], 'lvl': 23, 'name': 'drew'},
 {'items': ['sword', 'mace', 'health_potion'], 'lvl': 23, 'name': 'john'}]

使用列表理解过滤字典的正确方法是请求要过滤的字段。 key in dictionary 等同于 python2 中的 key in dictionary.keys()。正确的语法如下:

[card for card in deck if 'mace' in card['items']]

你也可以使用filter:

filter(lambda card: 'mace' in card['items'], deck)

如果您想过滤多个值,您可以使用 andor 链接测试来捕获您需要的子集:

[card for card in deck if 'mace' in card['items'] and card['lvl'] == 23]

如果过滤器变大,请创建一个函数。

在这种情况下,filter 没有提供比列表压缩更多的价值。 列表理解filter 对应部分更简短,更易于阅读。对于复杂的 filtering 情况,两者都需要一个函数来保持可读性。

deck 不是字典,而是字典列表,因此我认为您的搜索应该 return 符合搜索条件的字典列表。

有两种情况需要考虑:第一种是当您想要为特定键匹配特定值时 - 对于 lvlname 等属性,您将需要这种情况。第二个是您要在针对属性存储的列表中查找值的位置。例如,您将需要它来查明玩家是否携带了特定物品。

你实际上 知道 它们之间的区别是有意义的 - 大概 items 总是一个列表,而 lvl 总是一个整数? - 你可以像这样在附属字典中注册类型:

is_list = {'name': False,
           'lvl': False,
           'items': True}

如果必须通过检查项目的 type(),您可以动态地做出相同的决定,但是使用可变数据类型并不是很好的做法,因为它会使您的逻辑变得有些复杂。由于您向函数添加了一个 self 参数,我认为它是一种方法,但这与问题并不相关。

鉴于 is_list 的存在,您的逻辑可以定义一个 test_card 函数,如果卡片满足您的条件,该函数 return 为真,以及一个迭代的 filter_deck 函数在一副牌中 return 只收集至少满足其中一项条件的卡片。然后逻辑看起来像这样:

deck = [{
         'name': 'drew',
         'lvl': 23,
         'items': ['sword', 'axe', 'mana_potion']},
        {'name': 'john',
         'lvl': 23,
         'items': ['sword', 'mace', 'health_potion']},
        {'name': 'somethingelse',
         'lvl': 10,
          'items': ['health_potion']}]

is_list = {'name': False,
          'lvl': False,
          'items': True}

def test_card(card, name, values):
    if is_list[name]: # values is a list
        return any(value in card[name] for value in values)
    else:             # values is a single value
        return card[name] == values

def filter_deck(deck, **filters): # filters is a dict of keyword args
    r = []
    for d in deck:
        #print "Filtering", d, "on", filters
        if any(test_card(d, n, v) for n, v in filters.items()):
            r.append(d)
    return r

print filter_deck(deck, name="john")
print filter_deck(deck, name="drew", lvl=10)

输出

[{'lvl': 23, 'name': 'john', 'items': ['sword', 'mace', 'health_potion']}]
[{'lvl': 23, 'name': 'drew', 'items': ['sword', 'axe', 'mana_potion']}, {'lvl': 10, 'name': 'somethingelse', 'items': ['health_potion']}]

您可以通过在 filter_deck 中使用列表理解来进一步压缩逻辑,但这可能会使程序更难以阅读和理解。我总是首先尝试可读性。