Фильтрация объектов из коллекции на основе выбора пользователя / ввода - PullRequest
0 голосов
/ 11 января 2019

У меня довольно простое упражнение, но я ограничен своими знаниями и требованиями использовать шаблоны проектирования (+ юнит-тесты). Цель проекта - создать консольное приложение, которое позволит вам сохранять (добавлять), распечатывать (показывать все), удалять (удалять по критерии) и фильтровать (показывать по критериям) сообщения из коллекции.

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

Мне удалось создать функцию «добавить» и «показать все». Моя проблема с фильтрацией. Я должен создать опцию для фильтрации сохраненных объектов на основе критериев, заданных пользователем (все возможные комбинации, такие как: фильтр по заголовку и creationDate, фильтр по заголовку и т. Д.). Я подумал о том, чтобы дать пользователю возможность выбрать его из меню, используя переключатель и такие методы:

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());
}

но я думаю, что не стоит копировать много похожего кода. Кроме того, я могу оказаться в будущем, что такое решение будет утомительным и даже невозможным (каждый новый параметр будет добавлять новые комбинации). Есть ли лучший способ сделать это? Например, выбрать критерии «один за другим», чтобы пользователи могли сами создавать комбинации? У меня был совет, что предикаты могли бы помочь мне с этим, но я не знаю, куда идти с этим.

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Моя проблема с фильтрацией. Я должен создать опцию для фильтрации сохраненные объекты на основе критериев, заданных пользователем такие комбинации, как: фильтр по заголовку и creationDate, фильтр по заголовку и др.).

Это то, что вы можете попробовать, использовать или импровизировать на нем. Код является рабочим примером (использует Java SE 8). Пример имеет класс MessageFilter:

  • Создает List тестовых сообщений.
  • Принимает пользовательский ввод с консоли - поле сообщения и его значение. Например, "заголовок" и "сообщение 1".
  • На основании поля сообщения и его значения получает Predicate из 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;
    }
}
0 голосов
/ 11 января 2019

Для каждого критерия вы можете иметь BiPredicate<String, Message> 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<Message>, но это сделает эти функции менее изолированными, поскольку вам нужно сравнить контекст сообщения с заданным вводом.
2 Я использовал Java 9's Map.of.

...