当前位置: 首页 > 知识库问答 >
问题:

根据波动的条件数量过滤列表

解晟睿
2023-03-14

我想编写能够使用许多条件来过滤对象列表的代码,但我希望条件的数量波动(有时使用1个字段过滤,有时使用所有3个字段过滤)。

在我的情况下,我希望用户可以为某个字段选择“任何”,但仍然对其他字段应用过滤器。在下面的情况下,用户可以通过字段< code>x,y

我已经编写了下面的代码,可以在这种情况下工作,但在将来添加更多过滤器时效率会越来越低。(2个过滤器导致4个返回语句,3个过滤器导致8个返回语句等等)。

private static ArrayList<Model> getFilteredList(ArrayList<Model> originalList, String x, String y, String z ){

    if(x.equals("any") && y.equals("any") && z.equals("any")) {
        return originalList;
    }
    if(x.equals("any") && y.equals("any") && !z.equals("any")) {
        return originalList.stream().filter(item -> item.getZ().equals(z)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(x.equals("any") && !y.equals("any") && z.equals("any")) {
        return originalList.stream().filter(item -> item.getY().equals(y)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(x.equals("any") && !y.equals("any") && !z.equals("any")) {
        return originalList.stream().filter(item -> item.getY().equals(y)&& item.getZ().equals(z)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(!x.equals("any") && y.equals("any") && z.equals("any")) {
        return originalList.stream().filter(item -> item.getX().equals(x)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(!x.equals("any") && y.equals("any") && !z.equals("any")) {
        return originalList.stream().filter(item -> item.getX().equals(x)&& item.getZ().equals(z)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(!x.equals("any") && !y.equals("any") && z.equals("any")) {
        return originalList.stream().filter(item -> item.getX().equals(x)&& item.getY().equals(y)).collect(Collectors.toCollection(ArrayList::new));
    }
    if(!x.equals("any") && !y.equals("any") && !z.equals("any")) {
        return originalList.stream().filter(item -> item.getX().equals(x)&& item.getY().equals(y)&& item.getZ().equals(z)).collect(Collectors.toCollection(ArrayList::new));
    }
    return originalList;
}

以下是模型的外观:

public class Model {
    private String x;
    private String y;
    private String z;

    public String getX() {
        return x;
    }
    public String getY() {
        return y;
    }
    public String getZ() {
        return z;
    }
}

下面的代码将返回一个列表,其中x字段等于“red”,其中y字段等于“media”,其中z字段可以是任何东西。

private static ArrayList<Model> items = new ArrayList<>();

public static void main(String[] args) {
    ArrayList<Model> filteredList = getFilteredList(items, "red", "medium", "any");
}

我不确定是否有更简单的方法来执行我的getFilteredList方法,我已经搜索了很多,但找不到任何更简单/更有效的实现。在未来,我希望允许10个过滤器,其中“any”始终是一个选项,但我当前的解决方案太长,根本不会优雅。如果有人能为如何优雅地过滤具有不同数量过滤器的列表提供任何答案,那将不胜感激。

谢谢。

共有3个答案

翁和正
2023-03-14

您可以使用helper方法使用给定的字段和值构造谓词,然后合并这些谓词并将其应用于流:

private static ArrayList<Model> getFilteredList(ArrayList<Model> originalList, String x, String y, String z ){
    Predicate<Model> filter = Stream.of(compare(Model::getX, x), compare(Model::getY, y), compare(Model::getZ, z))
            .filter(Objects::nonNull)
            .reduce(Predicate::and)
            .orElse($ -> true);

    return originalList.stream()
            .filter(filter)
            .collect(Collectors.toCollection(ArrayList::new));
}

private static Predicate<Model> compare(Function<Model, String> field, String value) {
    return value.equals("any") ? null : m -> field.apply(m).equals(value);
}
胡鸿远
2023-03-14
匿名用户

你可以用

private static List<Model> getFilteredList(
    ArrayList<Model> originalList, String x, String y, String z ){

    if(Stream.of(x, y, z).allMatch("any"::equals)) return originalList;

    Stream<Model> s = originalList.stream();
    if(!"any".equals(x)) s = s.filter(m -> m.getX().equals(x));
    if(!"any".equals(y)) s = s.filter(m -> m.getY().equals(y));
    if(!"any".equals(z)) s = s.filter(m -> m.getZ().equals(z));
    return s.collect(Collectors.toList());
}

但是您应该决定返回类型。ArrayList 的显式返回类型表明调用方收到可变列表,但是当该列表有时是原始列表,有时不是时,根据筛选条件,修改具有未定义的语义,因为它们可能会影响原始列表,这将是灾难性的。

所以你必须决定

> < li>

返回的列表不应该被修改(不要将其声明为< code>ArrayList并且不要强制创建< code>ArrayList),这就是上面的示例所做的,或者

返回的列表应该是一个不影响源的可变列表,然后去掉所有返回原始列表的优化-"any",或者

原始列表应该在那之后未使用,或者

对返回列表的修改应该总是影响原始列表

对于c),合约不能在方法中强制执行,对于d),使用流API是不可能的。因此,对于c)或d),最好改为修改原始列表,例如

private static void filterList(ArrayList<Model> list, String x, String y, String z) {
    Predicate<Model> any = m -> false, effective = any;
    if(!"any".equals(x)) effective = m -> !m.getX().equals(x);
    if(!"any".equals(y)) effective = effective.or(m -> !m.getY().equals(y));
    if(!"any".equals(z)) effective = effective.or(m -> !m.getZ().equals(z));
    if(effective != any) list.removeIf(effective);
}

这并没有留下任何关于语义学的疑问。

夏令秋
2023-03-14

您可以仅为要过滤的每个String及其相应的Model属性链接流上的过滤器调用

stringFilterBiFunction确保如果该属性的过滤器是"any"或过滤器等于适当的Model字段中的值,则模型将包含在结果流中。
由于每个过滤器都对最后一个过滤器的输出进行操作,因此我们可以确保没有不满足所有所需条件的Model将包含在返回的列表中。

public List<Model> getFilteredList(List<Model> originalList, String x, String y, String z) {

    final BiPredicate<String, Supplier<String>> stringFilter = (filter, stringSupplier) ->
            filter.equals("any") || filter.equals(stringSupplier.get());

    return originalList.stream()
            .filter(model -> stringFilter.test(x, model::getX))
            .filter(model -> stringFilter.test(y, model::getY))
            .filter(model -> stringFilter.test(z, model::getZ))
            .collect(Collectors.toList());
}

供应商

当我们过滤流时,我们将要过滤的字符串之一和相应的Model getter传递给我们的BiPredicate。getter通过引用传递,并将作为我们的供应商的字符串源。

 类似资料:
  • 问题内容: 我有一个要点词典,说: 我想创建一个新字典,其中所有x和y值均小于5的点,即点“ a”,“ b”和“ d”。 根据这本书,每个字典都有该函数,该函数返回一个 元组列表: 所以我写了这个: 有没有更优雅的方式?我期待Python具有一些超棒的功能… 问题答案: 如今,在Python 2.7及更高版本中,您可以使用dict理解: 在Python 3中:

  • 问题内容: 我有包含单列和两列的数据行。我要做的是提取仅包含2列的行。 仅产生: 请注意,它们是制表符分隔的,即使对于只有一列的行,您也可以在其开头使用制表符。 怎么做呢? 我尝试了这个但是失败了: 问题答案: 您需要使用(字段数)变量来控制操作,例如以下记录: 如果字段数为2,将打印该行,否则将不执行任何操作。我之所以具有(看似)奇怪的构造,是因为如果没有任何规则匹配一行,则默认情况下将打印的某

  • 问题内容: 我有一个值列表,需要根据给定的布尔值列表中的值进行过滤: 我使用以下行生成一个新的过滤列表: 结果是: 这条线工作正常,但是(对我而言)看起来有点过大了,我想知道是否有更简单的方法来实现这一目标。 忠告 以下答案提供了两个好的建议: 1-不要像我一样命名列表,因为它是内置函数。 2-不要比较像我做的事情,因为这是不必要的。只需使用就足够了。 问题答案: 您正在寻找: 时序比较(py3.

  • 问题内容: 谁能向我解释为什么我对这两个表达式会得到不同的结果?我正在尝试在2个日期之间进行过滤: 结果:37M 与 结果:25M 它们有何不同?在我看来,他们应该产生相同的结果 问题答案: TL; DR 要传递多个条件或使用对象和逻辑运算符(,,)。请参见Pyspark:when子句中的多个条件。 您还可以使用 单个 SQL字符串: 实际上,在以下两者之间使用更有意义: 第一种方法甚至不是远程有

  • 问题内容: 我的服务器的CPU使用率异常高,我可以看到Apache使用了太多的内存。我有一种感觉,我被一个IP所包围-也许您可以帮助我找到他? 我使用以下行来查找10个最“活跃”的IP: 前5个IP对服务器的请求大约是“平均”用户的200倍。但是,我无法确定这5位访问者是否只是非常频繁的访问者,或者他们正在攻击服务器。 有没有办法将上述搜索指定到一个时间间隔,例如 最近两个小时还是今天的10到12

  • 问题内容: 我有一个字典列表和每个字典的key(比方说)“型”,这可以有值,等我的目标是过滤掉这些字典到同一个字典列表,但只有一个的那些某些“类型”。我想我真的很在理解问题。 因此,示例列表如下所示: 我有一个键值列表。举例来说: 预期的结果列表如下所示: 我知道我可以用一组for循环来做到这一点。我知道必须有一个更简单的方法。我发现此问题有很多不同的风格,但没有一个完全符合要求并回答了这个问题。