[英]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.