Список фильтров на основе отдельного и второго предиката - PullRequest
3 голосов
/ 15 июня 2019

Мой объект выглядит следующим образом

Store {
   String shopId;
   long distance;
}

У меня есть список магазинов.

List<Store> storesList = Arrays.asList(
    new Store (1, 1),
    new Store (1, 5),
    new Store (2, 2),
    new Store (1, 1), // this is duplicate
    new Store (1, 2),
    new Store (1, 1), // this is duplicate
    new Store (3, 7)
    new Store (3, 5)
);

выход

Store {shopId=1, distance=1}  // its fine to have any one among 3 duplicates
Store {shopId=2, distance=2}
Store {shopId=3, distance=5}

я могу вызвать свой собственный метод distint, например, следующий

private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

и отфильтруйте это так

List<Store> stores= storesList .stream()
        .filter(distinctByKey(pr -> Arrays.asList(pr.getShopId())))
        .collect(toList());

но как отфильтровать его одновременно по меньшему расстоянию?

Ответы [ 3 ]

4 голосов
/ 15 июня 2019
 storesList.stream()
           .collect(Collectors.toMap(
                Store::getShopId,
                Function.identity(),
                BinaryOperator.minBy(Comparator.comparingLong(Store::getDistance))
              ))
           .values()
           .forEach(System.out::println);

Вы можете объединить эти самые Store с (storeId), где вы скажете, что при объединении вы получите наименьшее distance между двумя магазинами.

2 голосов
/ 15 июня 2019

Если вы отсортируете поток по расстоянию до фильтра, вы получите наименьшее расстояние:

List<Store> stores = storesList.stream()
        .sorted(Comparator.comparing(Store::getDistance))
        .filter(distinctByKey(it -> it.shopId))
        .collect(toList());

0 голосов
/ 15 июня 2019

Вы можете попробовать это:

Collection<Store> stores = storesList.stream()
        .collect(Collectors.groupingBy(Store::getShopId, 
                Collectors.collectingAndThen(
                        Collectors.minBy(Comparator.comparingLong(Store::getDistance)), 
                        Optional::get)))
        .values();

Сначала вы группируете по shopId, затем вы используете значение с минимальным значением distance. В Ende вы просто используете значения этой карты в качестве результата.

Если вам нужен Список вместо Коллекции, вы можете просто использовать

new ArrayList<>(stores);
...