Метод вычисления наиболее частой фамилии из списка заданных пользователей с помощью Java Stream API - PullRequest
2 голосов
/ 15 марта 2019

Функция должна возвращать необязательную наиболее часто встречающуюся фамилию (если она встречалась как минимум два раза) или необязательную пустую, если число фамилий одинаково или список пользователей пуст

Это то, что я придумалс, но не возвращает Optional.empty

@Override
public Optional<String> getMostFrequentLastName(final List<User> users) {
            return users.stream()
                .map(User::getLastName)
                    .distinct()
                .collect
                        (Collectors.groupingBy(
                                Function.identity(),
                                Collectors.summingInt(w -> 1)
                        ))
                    .entrySet()
                    .stream()
                    .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
                    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .map(Map.Entry::getKey)
                    .findFirst();
}

Это мой тестовый класс

public static void main(String[] args) {
    Optional<String> optionalS = Stream.of(new User("name1"),
             new User("name1"), new User("name2"), new User("name2"))
            .map(User::getLastName)
            .collect
                    (Collectors.groupingBy(
                            Function.identity(),
                            Collectors.counting()
                    ))
            .entrySet()
            .stream()
            .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
            .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
            .map(Map.Entry::getKey)
            .findFirst();
    System.out.println(optionalS.toString());
}

Вот awnser

Optional[name2]

Но должно быть

Optional[empty]

Ответы [ 4 ]

2 голосов
/ 15 марта 2019

Вы можете использовать

Optional<String> optionalS =
Stream.of(new User("name1"), new User("name1"), new User("name2"), new User("name2"))
        .collect(Collectors.groupingBy(User::getLastName, Collectors.counting()))
        .entrySet()
        .stream()
        .filter(entry -> entry.getValue() >= 2)
        .reduce((e1, e2) -> e1.getValue() < e2.getValue()? e2:
                            e1.getValue() > e2.getValue()? e1:
                            new AbstractMap.SimpleImmutableEntry<>(null, e1.getValue()))
        .map(Map.Entry::getKey);

System.out.println(optionalS.toString());

Получение максимального значения является формой Сокращение . Так как вы хотите получить пустой необязательный параметр в случае связывания, самое простое решение - явно написать функцию сокращения, используйте Map.Entry с большим значением, если оно есть, в противном случае создайте новый Map.Entry с * 1008. * ключ.

Результатом сокращения уже является Optional, который будет пустым, если не было элементов (с количеством >=2). Таким образом, последний шаг map применяется к Optional. Если уже пусто, функция map не будет оценена, а полученный Optional останется пустым. Если необязательный параметр не пустой, но Map.Entry::getKey оценивается как null, результирующий необязательный будет пустым.

1 голос
/ 15 марта 2019

Это мне кажется , что если у вас одинаковое количество максимумов нескольких разных lastNames, вы хотите вернуть Optional::empty, как таковое:

Map<String, Long> map =
     Stream.of(new User("name1"),
               new User("name1"),
               new User("name2"),
               new User("name2"))
            .collect(Collectors.groupingBy(User::getLastName, Collectors.counting()));

map.entrySet()
   .stream()
   .max(Entry.comparingByValue())
   .flatMap(en -> {
           boolean b = map.entrySet()
                          .stream()
                          .filter(x -> !x.getKey().equals(en.getKey()))
                          .mapToLong(Entry::getValue)
                          .noneMatch(x -> x == en.getValue());
           return b ? Optional.of(en.getKey()) : Optional.empty();
       })
  .ifPresent(System.out::println);
}
0 голосов
/ 15 марта 2019

Вот мой монстр для тебя:

    Optional<String> optionalS = Stream.of(
            new User("name1"),
            new User("name1"),
            new User("name2"),
            new User("name2"))
            .map(User::getLastName)
            .collect(
                    Collectors.groupingBy(
                            Function.identity(),
                            Collectors.counting()
                    ))
            .entrySet()
            .stream()
            .filter(stringIntegerEntry -> stringIntegerEntry.getValue() >= 2)
            .collect(
                    Collectors.groupingBy(
                            Map.Entry::getValue,
                            Collectors.toList()
                    ))
            .entrySet()
            .stream()
            .sorted(Comparator.comparing(
                    Map.Entry::getKey,
                    Comparator.reverseOrder()))
            .map(Map.Entry::getValue)
            .findFirst()
            .filter(x -> x.size() == 1)
            .map(x -> x.get(0).getKey());

    System.out.println(optionalS);
0 голосов
/ 15 марта 2019

Насколько я понимаю ваше решение в потоке, ваш код создает

Map<String(lastname),Integer(number of occurence)> 

и затем отфильтруйте эту карту, где число вхождений> = 2, и в вашем тестовом примере у вас есть карта с записями:

<"name1",2>
<"name2",2>

Таким образом, упорядочение по значению будет по-прежнему возвращать два значения.

Вы должны попробовать создать

Map<Integer,List<String>> 

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

// редактировать

Ниже приведен короткий фрагмент с моим решением:

  Map<Integer, List<String>> map = new HashMap<>();
    map.put(2,Arrays.asList("name1","name2"));

    Optional<String> optionalS = map
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
            .findFirst() //get max{map's keys}
            .filter(x->x.getValue().size() == 1) //get lastname that occured only once
            .map(x->x.getValue().get(0)); //get that lastname (above filter check that list has only one element) or Optional.empty if stream didn't find any

    System.out.println(optionalS.toString());

Я пропустил часть создания карты.

P.S. Вы можете заменить HashMap на TreeMap на собственный компаратор, чтобы избежать сортировки в потоке.

...