简体   繁体   English

在Python列表中搜索多个条件

[英]Multiple criteria search in a list in Python

dataset: 数据集:

data = [{'name':'kelly', 'attack':5, 'defense':10, 'country':'Germany'}, 
        {'name':'louis', 'attack':21, 'defense': 12, 'country':'france'}, 
        {'name':'ann', 'attack':43, 'defense':9, 'country':'Germany'}]

header = ['name', 'attack', 'defense', 'country']

filter_options = {'attack':4, 'defense':7, 'country':'Germany'}

I would like to write a function whereby data is the argument and filter_options is the parameters of the function. 我想编写一个函数,其中data是参数,而filter_options是函数的参数。 ie func(data, filter_options) func(data, filter_options)

The filter_options will filter by exact match for string type values, and/or filter continuous variables specified value that is greater than or equal to the dictionary key parameter. filter_options将根据字符串类型值的完全匹配进行过滤,和/或过滤大于或等于字典关键字参数的指定连续值。 ie my answer should be 即我的答案应该是

answer = [{'name':'kelly', 'attack':5, 'defense':10, 'country':'Germany'},
          {'name':'ann', 'attack':43, 'defense':9, 'country':'Germany'}]

my current code: 我当前的代码:

search_key_list = [key for key in filter_options.keys()]
header_index_list = [header.index(i) for i in search_key_list if i in header]

answer = []
for i in header_index_list:
    for d in data:
        if type(filter_options[header[i]]) == int or type(filter_options[header[i]]) == float:
            if data[header[i]]>filter_options[header[i]]:
                answer.append(d)
        elif type((filter_options[header[i]])) == str:
            if data[header[i]] == filter_options[header[i]]:
                answer.append(d)

The code is wrong because it is not considering the multiple criteria. 该代码是错误的,因为它没有考虑多个条件。 It is looking at one criteria, checking which sublist fits the criteria, append the sublist to the answer list and then moving on to the next criteria. 它正在查看一个条件,检查哪个子列表符合条件,然后将该子列表附加到答案列表中,然后转到下一个条件。

How can I correct this? 我该如何纠正? Or what other codes will work? 或者还有哪些其他代码有效?

You need to check all "filters" and only append it if all of the filters match the dataset: 您需要检查所有“过滤器”,并且仅在所有过滤器均与数据集匹配时才附加它:

data = [{'name':'kelly', 'attack':5, 'defense':10, 'country':'Germany'}, 
        {'name':'louis', 'attack':21, 'defense': 12, 'country':'france'}, 
        {'name':'ann', 'attack':43, 'defense':9, 'country':'Germany'}]

header = ['name', 'attack', 'defense', 'country']

filter_options = {'attack':4, 'defense':7, 'country':'Germany'}


def filter_data(data, filter_options):
    answer = []
    for data_dict in data:
        for attr, value in filter_options.items():  # or iteritems
            if isinstance(value, (int, float)):     # isinstance is better than "type(x) == int"!
                if data_dict[attr] < value:         # check if it's NOT a match
                    break                           # stop comparing that dictionary
            elif isinstance(value, str):
                if data_dict[attr] != value:
                    break
        # If there was no "break" during the loop the "else" of the loop will
        # be executed
        else:
            answer.append(data_dict)
    return answer


>>> filter_data(data, filter_options)
[{'attack': 5, 'country': 'Germany', 'defense': 10, 'name': 'kelly'},
 {'attack': 43, 'country': 'Germany', 'defense': 9, 'name': 'ann'}]

The trick here is that it checks if it's smaller (in case it's an integer) or unequal (for strings) and then immediately stops comparing that dictionary and when the loop wasn't break ed and only then it appends the dictionary. 这里的窍门是,它检查它是较小的(如果是整数)还是不相等的(对于字符串),然后立即停止比较该字典,并且在循环未break时才附加字典。


Another way without using an else clause for the loop would be: 在循环中不使用else子句的另一种方法是:

def is_match(single_data, filter_options):
    for attr, value in filter_options.items():
        if isinstance(value, (int, float)):
            if single_data[attr] < value:
                return False
        elif isinstance(value, str):
            if single_data[attr] != value:
                return False
    return True

def filter_data(data, filter_options):
    answer = []
    for data_dict in data:
        if is_match(data_dict, filter_options):
            answer.append(data_dict)
    return answer

filter_data(data, filter_options)

You could also use a generator function instead of manual appends (based on the first approach): 您还可以使用生成器函数代替手动附加(基于第一种方法):

def filter_data(data, filter_options):
    for data_dict in data:
        for attr, value in filter_options.items():
            if isinstance(value, (int, float)):
                if data_dict[attr] < value: 
                    break          
            elif isinstance(value, str):
                if data_dict[attr] != value:
                    break
        else:
            yield data_dict
    return answer

However that requires casting it a list afterwards: 但是,这需要在之后强制转换为list

>>> list(filter_data(data, filter_options))

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

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