简体   繁体   English

在dict中按值过滤项目

[英]filter items by value in dict

I have a dict setup like so: 我有这样的字典设置:

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

This is a basic example of what it looks like, I need a way to filter (copy only the {characters}) that match certain values, such as I want only characters that are level 23, or that are carrying a sword. 这是它的外观的基本示例,我需要一种方法来过滤(仅复制{characters})匹配某些值,例如我只想要23级的字符,或者携带剑的字符。

I was looking at doing something like this: 我正在做这样的事情:

filtered = filter_deck(deck, 'mace')

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

and return: 并返回:

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

I am not sure how to filter either a specific item like k:v or k:[v1,v2,v3] when I do not know if it is a single value, or a list of values, or how to filter multiple values. 我不知道如何过滤特定项目,如k:v或k:[v1,v2,v3],当我不知道它是单个值,还是值列表,或如何过滤多个值。

I am not sure how I can filter character's with multiple keys. 我不知道如何用多个键过滤字符。 Say that I want to sort out characters that are lvl 23, or have items['sword'] or items['mace']. 假设我要整理lvl 23中的字符,或者有项目['sword']或项目['mace']。 How would I have it sort in a way filter_cards(deck, ['lvl'=23, 'items'=['sword','mace']) 我怎么能以某种方式排序filter_cards(deck, ['lvl'=23, 'items'=['sword','mace'])

So if any character is lvl 23, or carries a mace or a sword, they are on that list. 因此,如果任何角色是lvl 23,或携带钉锤或剑,他们就在那个名单上。

You could just use the standard filter and pass in the filter function, eg: 您可以使用标准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)

Etc. These return filter generators so you if you want to print them out turn them into a list: 等等。这些返回过滤器生成器,因此如果要打印它们,请将它们转换为列表:

>>> 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'}]

You could also break out the lambda into a standard function: 您还可以将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'}]

Your deck is a list (of dictionaries) , it does not have .items() . 你的deck是一个(词典)列表,它没有.items() so trying to do - deck.items() would fail. 所以试图做 - deck.items()会失败。

Also the syntax - 语法 -

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

is invalid , You should use a dictionary as the second element. 无效,您应该使用字典作为第二个元素。 Example - 示例 -

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

You should use filter() built-in function, with a function that returns True, if the dictionary contains one of the values. 如果字典包含其中一个值,则应使用filter()内置函数,其函数返回True。 Example - 示例 -

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))

Demo - 演示 -

>>> 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'}]

The correct way to filter dict using list comprehension is by requesting the field that you want to filter against. 使用列表推导过滤dict的正确方法是请求要过滤的字段。 key in dictionary is equivalent to key in dictionary.keys() in python2. key in dictionary中的key in dictionary.keys()等效于python2 key in dictionary.keys()中的key in dictionary.keys() The correct syntax is the following: 正确的语法如下:

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

You can also use filter : 您还可以使用filter

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

If you want to filter against multiple values, you can chain tests using and and or to capture the subset your need: 如果要过滤多个值,可以使用and来链接测试, or捕获所需的子集:

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

If the filter gets bigger create a function. 如果过滤器变大,则创建一个函数。

In this case filter doesn't provide more value than list comprension. 在这种情况下, filter器不会提供比列表强度更多的值。 List comprehension are easier to read when they are short, more than their filter counter part. 当列表理解很短时,它们比filter计数器部分更容易阅读。 And for the complex filtering cases, both requires a function to stay readable. 对于复杂的过滤情况,两者都需要一个保持可读性的功能。

deck isn't a dict, but a list of dicts, so I presume your search should return a list of the dicts that match your search criteria. deck不是dict,而是一个dicts列表,所以我假设你的搜索应该返回一个符合你搜索条件的dicts列表。

There are two cases to consider: the first is when you want to match a specific value for a particular key - which you will need for attributes like lvl and name . 有两种情况需要考虑:第一种情况是当您想要匹配特定键的特定值时 - 您将需要lvlname等属性。 The second is where you are looking for a value in the list stored against an attribute. 第二个是您在针对属性存储的列表中查找值。 You'll need this, for example, to find out if a player is carrying a particular item. 例如,您需要这样才能确定玩家是否携带特定物品。

It would make sense for you to actually know the difference between these - presumably items is always a list, and lvl is always an integer? 你真的知道它们之间的区别是有意义的 - 大概是items总是一个列表,而lvl总是一个整数? - and you can register the types in a subsidiary dict like this: - 你可以在这样的子词典中注册类型:

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

You could make the same decision dynamically if you had to by examining the type() of an item, but it's not very good practice to have variable data types as it complicates your logic somewhat. 如果必须通过检查项目的type() ,可以动态地做出相同的决定,但是拥有可变数据类型并不是很好的做法,因为它会使您的逻辑有些复杂化。 Since you add a self argument to your function I presume it's a method, but that's not really relevant to the question. 由于你为函数添加了一个self参数,我认为它是一个方法,但这与问题并不相关。

Given the presence of is_list your logic could define a test_card function that returns True if the card meets your criteria, and a filter_deck function that iterates over the deck returning only those cards that meet at least one of the criteria. 鉴于is_list的存在,您的逻辑可以定义一个test_card函数,如果卡符合您的条件,则返回True,并且迭代在套牌上的filter_deck函数仅返回满足至少一个条件的卡。 The logic would then look something like this: 然后逻辑看起来像这样:

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)

which outputs 哪个输出

[{'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']}]

You could compress the logic more by using a list comprehension in filter_deck but that might make the program more difficult to read and understand. 您可以通过在filter_deck使用列表filter_deck来更多地压缩逻辑,但这可能会使程序更难以阅读和理解。 I always try for readability first. 我总是首先尝试可读性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM