根据用户选择/输入从集合中过滤对象

Posted

技术标签:

【中文标题】根据用户选择/输入从集合中过滤对象【英文标题】:Filtering objects from collection based on user choice/input 【发布时间】:2019-06-05 20:44:13 【问题描述】:

我有一个相当简单的练习,但我受到我的知识和使用设计模式(+单元测试)的要求的限制。项目的目标是创建一个控制台应用程序,该应用程序允许您从集合中保存(添加)、打印(显示全部)、删除(按 cryteria 删除)和过滤(按条件显示)消息。

private String title;
private String author;
private String content;
private String creationDate;

我能够创建“添加”功能和“显示全部”。我的问题是过滤。我必须创建一个选项来根据用户给出的标准过滤保存的对象(所有可能的组合,如:按标题和创建日期过滤,按标题过滤等)。我想过让用户可以通过使用 switch 和这样的方法从菜单中选择它:

private final List<Message> storage = new ArrayList<Message>();

public List<Message> getAll() 
    final ArrayList<Message> messages = new ArrayList<>();
    messages.addAll(storage);
    return messages;

List<Message> find(String author) 
    return simpleStorage.getAll().stream()
        .filter(item -> item.getAuthor() == author)
        .collect(toList());

但我认为复制大量类似代码并不是一个好习惯。另外,我将来可能会发现这样的解决方案会令人厌烦甚至是不可能的(每个新参数都会添加新的组合)。有更好的方法吗?就像“一个一个”地选择一个标准,以便用户可以自己创建组合?我有一个提示,谓词可以帮助我解决这个问题,但我不知道该去哪里。

【问题讨论】:

【参考方案1】:

我的问题在于过滤。我必须创建一个过滤选项 根据用户给出的标准保存的对象(所有可能的 组合,如:按标题和创建日期过滤,按标题过滤 等等)。

这是您可以尝试、使用或即兴创作的东西。该代码是一个工作示例(使用 Java SE 8)。该示例具有MessageFilter 类:

创建一个List 的测试消息。 接受来自控制台的用户输入 - 消息字段及其值。例如,“标题”和“消息 1”。 根据消息字段及其值从 getPredicate 方法。 谓词应用于消息并打印过滤结果。

该示例显示了按“标题”和“作者”进行过滤 - 分别。我认为示例中的概念可以应用于其他过滤条件。

示例代码:

class Message  // represents a message

    private String title;
    private String author;

    Message(String s1, String s2) 
        title = s1;
        author = s2;
    

    String getTitle() 
        return title;
    
    String getAuthor() 
        return author;
    

    public String toString() 
        return String.join(", ", "("+title, author+ ")");
    


public class MessageFilter 

    public static void main(String [] args) 

        // Create some messages

        Message [] array = new Message("msg1", "auth1"),
                            new Message("msg2", "auth2"),
                            new Message("msg3", "auth1"),
                            new Message("msg9", "auth3")
                            ;
        List<Message> messages = Arrays.asList(array);
        System.out.println(messages);

        // Accept user input: the field name and its value

        Scanner scanner = new Scanner(System.in);

        System.out.print("Enter the property to filter (title, author, etc): ");
        String filterCriteria = scanner.nextLine();
        System.out.print("Enter the property value: ");
        String filterValue = scanner.nextLine();

        // Get the predicate based on user input

        Predicate<Message> predicate = getPredicate(filterCriteria, filterValue);

        // Filter the data using the predicate got from user input, and print...

        List<Message> result = messages.stream()
                                        .filter(predicate)
                                        .collect(Collectors.toList());

        System.out.println("Result: " + result);
    

    private static Predicate<Message> getPredicate(String criteria, String value) 

        Predicate<Message> p = msg -> true; // by default returns all messages

        switch(criteria) 
            case "title":
                p = msg -> msg.getTitle().equals(value);
                break;
            case "author":
                p = msg -> msg.getAuthor().equals(value);
                break;  
        

        return p;
    

【讨论】:

【参考方案2】:

对于每个条件,您可以有一个BiPredicate&lt;String, Message&gt;1,它接受来自用户的原始输入和一条消息,并判断该消息是否与过滤选项匹配2.

Map<String, BiPredicate<String, Message>> criteria = Map.of(
        "title", (userTitle, message) -> input.equals(message.getTitle())
        ...
);

我会给你一个如何使用该地图的简化示例:

Scanner scanner = new Scanner(System.in);

String filteringOption = scanner.nextLine();
String userInput = scanner.nextLine();

BiPredicate<String, Message> predicate = criteria.get(filteringOption);

// get all messages from the storage
getAll()  
        // make a stream out of them
        .stream()
        // apply the filtering rule from the map
        .filter(m -> predicate.test(userInput, m))  
         // collect them into a list to display
        .collect(Collectors.toList());

稍后,这些谓词可以通过or()and() 等逻辑操作组合在一起,形成自定义过滤选项。用户的选项可以添加到地图中以进行后续调用或在每次用户请求时即时计算,例如

BiPredicate<String, Message> titleAndDateFilter = 
    criteria.get("title").and(criteria.get("date"));

1您可以使用Predicate&lt;Message&gt;,但它会使这些功能不那么孤立,因为您需要将消息的上下文与给定的输入进行比较。2 我用的是 Java 9 的 Map.of

【讨论】:

在早期 Java 版本中是否有 Map.of 的替代方案?还是类似于 Java 8 之前的 lambdas 的问题?我问这个问题是因为我目前正在使用 Java 8 进行项目,虽然我可以更改它,但我对它的一般工作方式很感兴趣。 @DrAhzek,您可以使用 new HashMap&lt;&gt;(),然后使用 Map#put(key, value) 填充 - 它不那么简洁。

以上是关于根据用户选择/输入从集合中过滤对象的主要内容,如果未能解决你的问题,请参考以下文章

如何从dataTable行集合中获取复选框输入值?

从 mongodb 集合中过滤掉相关产品

如何根据另一个的选定项目过滤一个组合框集合?

前端学习笔记之jQuery选择器一

根据特定日期和时间从数据池中的 mongo 集合中过滤数据在同一天的不同时间

使用 asp.net C# 和 jquery 过滤数据集合