Хороший способ отфильтровать список по недвижимости и по дате - PullRequest
0 голосов
/ 05 декабря 2018

У меня есть очень простая вещь, у меня есть список таких людей:

[{
    name: John,
    date: 01-01-2018,
    attend: true
},
{
    name: Adam,
    date: 01-01-2018,
    attend: false
},
{
    name: Adam,
    date: 01-02-2018,
    attend: true
},
{
    name: JOHN,
    date: 01-02-2018,
    attend: false
}]

Результатом этого массива должно быть: Адам (правда), Джон (ложь)

Поэтому мне нужно вернуть список последних записей от пользователей, в этом случае Джон сначала подтвердил, что он посещает, затем он передумал и сказал, что он не посещает, поэтому я возвращаю его последнюю запись (обратите внимание, что иногда это написано ДЖОНи иногда Джон, но это один и тот же человек, это довольно сложная часть)

Мой вопрос, как лучше всего отфильтровать этот вид списка, я подумал, применяя «уникальный по свойству поток Java», но сначаланужно будет упорядочить лиц по убыванию даты и по именам (в верхний / нижний регистр), а затем мне нужно будет как-то взять последнюю запись.

У кого-нибудь есть какая-нибудь хорошая идея, как лучше всего к этому подойти?

Ответы [ 4 ]

0 голосов
/ 05 декабря 2018

Компактный способ без потоков:

Map<String, User> map = new LinkedHashMap<>();
users.forEach(u -> map.merge(
        u.getName().toLowerCase(), 
        u, 
        BinaryOperator.maxBy(Comparator.comparing(Person::getDate))));

Collection<User> result = map.values();

Или, если вам нужно List:

List<User> result = new ArrayList<>(map.values());

Этот код использует Map.merge, который помещает запись вmap, если нет записи с таким же ключом (имя пользователя в нижнем регистре), или, если карта уже содержит запись для ключа, она применяет функцию слияния, которая в этом случае выбирает экземпляр User с максимальным значением date.

0 голосов
/ 05 декабря 2018

Вы можете использовать toMap сборщик:

Collection<Person> values = source.stream()
                    .collect(toMap(e -> e.getName().toLowerCase(),
                            Function.identity(),
                            BinaryOperator.maxBy(Comparator.comparing(Person::getDate))))
                    .values();

см. Этот ответ для объяснения того, как работает карта

0 голосов
/ 05 декабря 2018

Несмотря на то, что все ответы до сих пор являются функционально правильными, пожалуйста, рассмотрите эту опцию:

final Map<String, Boolean> lastAttendResults =
            people
                .stream()
                .collect(
                    groupingBy(
                        Person::getName, // Define what is the property you are using to group people.
                        () -> new TreeMap<String, Boolean>(String.CASE_INSENSITIVE_ORDER), // Supply a map implementation that ignore name case.
                        collectingAndThen(maxBy(Comparator.comparing(Person::getDate)), // Foreach list of grouped people, select that with last date.
                            o -> o.get().isAttend()))); // As maxBy returns an Optional<Person> and we are sure that it exists, just get the Person and if he attends.

Эта реализация интересна тем, что позволяет понять концепцию группировки коллекций.Хотя, по сути, он также использует карту, мне кажется, что использовать карту или нет - это не проблема программиста, я имею в виду, что мы ищем, как сгруппировать людей по имени и затем получить последнюю запись.

Если вы предпочитаете получать Список людей вместо Карты имени и посещаемости, вы можете использовать:

final List<Person> lastAttendResults =
        new ArrayList<>(people
            .stream()
            .collect(
                groupingBy(Person::getName, // Define what is the property you are using to group people.
                    () -> new TreeMap<String, Person>(String.CASE_INSENSITIVE_ORDER), // Supply a map implementation that ignore name case.
                    collectingAndThen(maxBy(Comparator.comparing(Person::getDate)), // Foreach list of grouped people, select that with last date.
                        Optional::get // As maxBy returns an Optional<Person> and we are sure that is exists, just get the Person.
                        ))).values());
0 голосов
/ 05 декабря 2018

Вы можете использовать Collectors.toMap, чтобы сделать то же самое, что и:

List<Person> finalList = new ArrayList<>(people.stream()
        .collect(Collectors.toMap(a -> a.getName().toLowerCase(),  // name in lowercase as the key of the map (uniqueness)
                Function.identity(), // corresponding Person as value
                (person, person2) -> person.getDate().isAfter(person2.getDate()) ? person : person2)) // merge in case of same name based on which date is after the other
        .values()); // fetch the values

Примечание : приведенное выше предполагает минимальный Person класс будет

class Person {
    String name;
    java.time.LocalDate date;
    boolean attend;
    // getters and setters
}
...