【问题标题】:Multiple criteria search in a list in PythonPython中列表中的多个条件搜索
【发布时间】:2017-09-02 18:06:59
【问题描述】:

数据集:

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

我想写一个函数,data 是参数,filter_options 是函数的参数。即func(data, filter_options)

filter_options 将通过精确匹配过滤字符串类型值,和/或过滤大于或等于字典键参数的连续变量指定值。即我的答案应该是

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

我当前的代码:

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)

代码是错误的,因为它没有考虑多个标准。它正在查看一个标准,检查哪个子列表符合标准,将子列表附加到答案列表,然后继续下一个标准。

我该如何纠正这个问题?或者还有哪些其他代码可以使用?

【问题讨论】:

    标签: python python-2.7 list dictionary filter


    【解决方案1】:

    您需要检查所有“过滤器”,并且只有在所有过滤器都与数据集匹配时才附加它:

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

    这里的技巧是它检查它是否更小(如果它是整数)或不相等(对于字符串),然后立即停止比较该字典并且当循环不是 breaked 时,它才会附加字典。


    在循环中不使用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)
    

    您也可以使用生成器函数代替手动追加(基于第一种方法):

    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
    

    但是,这需要在之后将其转换为 list

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

    【讨论】:

    • 感谢您的帮助!但是,for-else 语句的作用是什么?您的解释是,如果循环期间没有“中断”,则将执行循环的“else”。如果 "else" 语句被删除,answer.append(data_dict) 不会只在没有 "break" 的情况下运行吗?
    • 其实就像我说的:如果你没有break 退出循环(并且你没有通过return 等退出循环)循环的else被执行(另见break and continue Statements, and else Clauses on Loops)。它实际上是需要的,因为break 只结束最内层的循环,所以它仍然会执行循环之后的任何操作(else 除外)。另一种方法是在那里使用另一个函数。
    • @TimOng 我用一些替代方法更新了答案。
    猜你喜欢
    • 2013-11-18
    • 1970-01-01
    • 1970-01-01
    • 2011-07-05
    • 2020-12-19
    • 2021-09-14
    • 2019-04-10
    • 2010-09-21
    相关资源
    最近更新 更多