【问题标题】:Filtering dictionary with multiple possible comparisons过滤具有多个可能比较的字典
【发布时间】:2021-01-26 09:14:49
【问题描述】:

我正在开发一个网站,它部分采用 json 字典,其中每个项目都有“coder”、“category”等键,并采用某些 url 参数以按名称过滤它类别或编码器的名称。如果用户不过滤,这些 URL 参数可以是 none,如果用户只过滤特定信息,则可以是部分参数。

但是,我想不出一个干净的方法来按这些值过滤字典。 我期望能够做的是传递“coder”和“category”的值,并创建一个新字典,其中包含与其中指定的内部值匹配的所有内容,并按原样返回整个字典,如果没有指定参数.

这是我现在拥有的一些代码:

def filter_packages(packages, developer=None, category=None):
newpackages = []
for package in packages:
    if developer and category:
        if package["coder"] == developer:
            if package["category"] == category:
                newpackages.append(package)
    elif developer:
        if package["coder"] == developer:
            newpackages.append(package)
    elif category:
        if package["category"] == category:
            newpackages.append(package)
    else:
        newpackages.append(package)

return newpackages

此代码不可行,因为它杂乱、冗长且重复。用户能够应用的过滤器越多,我必须添加的案例就越多。 我想知道如何使用更简洁、更具可扩展性的代码来实现这一点。

【问题讨论】:

  • 是for循环中的返回吗?还是应该只是追加,然后在迭代所有包后返回?
  • @sim 啊这不是故意的,在澄清这个问题时忘记删除它。
  • 我认为你应该使用规范设计模式,它是专门为处理这种情况而设计的。这样做的主要优点是,如果您决定添加更多过滤器,而不是重写所有内容,您只需为新的过滤器类型扩展类。此设计模式从 5 个可靠的设计原则中指定了 OCP 的使用。您可以在网络上找到许多资源来实现这一点。

标签: python json python-3.x dictionary


【解决方案1】:

这是一个做出一个假设的答案:您传入的关键字参数与字典键具有相同的名称(在您的示例中不是这种情况,但您可以使用带有关键字参数名称和输出字典键名 - 就个人而言,我宁愿调整关键字参数的名称以匹配字典键)。

import operator
from functools import reduce

def build_dictionary_filter_from_kwargs(**filter_kwargs):
    def _filter_fun(field, value):
        return lambda d: d[field] == value
    return [_filter_fun(k, v) for k, v in filter_kwargs.items() if v is not None]

def filter_packages(packages, **filter_kwargs):
    new_packages = []
    filters = build_dictionary_filter_from_kwargs(**filter_kwargs)
    for package in packages:
        if len(filters) == 0 or reduce(operator.and_, [filter_(package) for filter_ in filters]):
            new_packages.append(package)
    return new_packages

例子:

filter_packages(packages=[{"developer": "foo",
                           "package": "bar"},
                          {"developer": "foo2",
                           "package": "bar"}],
                package="bar",
                developer="foo")

# [{'developer': 'foo', 'package': 'bar'}]

filter_packages(packages=[{"developer": "foo",
                           "package": "bar"},
                          {"developer": "foo2",
                           "package": "bar"}],
                package="bar")

# [{'developer': 'foo', 'package': 'bar'},
#  {'developer': 'foo2', 'package': 'bar'}]

连接是任意堆叠的,所以它是可扩展的。

编辑:关于如何使用:

import requests
packages = requests.get("https://api.oscwii.org/v2/primary/packages").json()

filter_packages(packages,
                coder="Danbo",
                downloads=0)

它应该可以在不调整当前函数签名的情况下工作(除了将developer 参数重命名为coder。当然,在上面的实现中,您只能支持检查相等性(和逻辑合取)。对于更复杂的您确实可以按照 cmets 中的建议查看规范设计模式。

【讨论】:

  • 您能否详细说明我如何才能使其适用于我的案例?为了更清楚,我正在使用的 json 字典可以在这里找到:api.oscwii.org/v2/primary/packages
  • 添加了基于包列表的示例。
  • 这似乎可行,谢谢!只有一个半题外话:你知道我怎么能把“”(空字符串)当作无?因为在某些条件下,这是 Flask 在不指定 url 参数时返回的内容。
  • 你可以把return [_filter_fun(k, v) for k, v in filter_kwargs.items() if v is not None]改为return [_filter_fun(k, v) for k, v in filter_kwargs.items() if v],然后None,空字符串也不会产生过滤条件。
【解决方案2】:

您可以将过滤键作为映射:

def filter_packages(packages, filter_keys_map):
  newpackages = []
  for package in packages:
    num_matching_keys = 0
    for key, val in filter_keys_map.items():
      if package[key] == val:
        num_matching_keys += 1
    if num_matching_keys == len(filter_keys_map):
        newpackages.append(package)
  return newpackages   


filter_keys_map = {}
filter_keys_map["coder"] = developer
filter_keys_map["category"] = category
# Make sure to not have a dict entry with null value; in that case remove its key entirely.
filter_packages(packages, filter_keys_map)

【讨论】:

  • 这不会产生正确的结果。如果同时提供了 coder 和 category,则两者都必须匹配才能添加一个包。
  • 除了上面注释中的说明外,如果没有指定编码器和类别,它也不会返回任何包。
  • @sim 我认为只有其中 1 个必须匹配才能包含该软件包;这就是我添加continue 的原因。修好了。
  • @GuyTwig 已修复。
猜你喜欢
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 2020-12-13
  • 2021-06-14
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
相关资源
最近更新 更多