Как можно объединить 2 списка, используя потоки и функции 1.8? - PullRequest
2 голосов
/ 07 марта 2019

У меня есть List цен и ценовых групп.

static class PriceGroup {
    String priceName;
    String priceGroup;
}

static class Price {
    String priceName;
    Integer price;
}

Ниже приведены некоторые примеры данных и одна реализация, которую я написал, чтобы найти самую низкую цену для каждой PriceGroup. Любые предложения, как я мог бы реорганизовать это, чтобы использовать потоки для объединения этих данных?

List<PriceGroup> priceGroups = Arrays.asList(
        new PriceGroup("F1", "Friends"),
        new PriceGroup("F2", "Friends"),
        new PriceGroup("O1", "Others"),
        new PriceGroup("O2", "Others"));

List<Price> prices = Arrays.asList(
        new Price("F1", 100),
        new Price("F2", 150),
        new Price("O1", 250),
        new Price("O2", 300));

public Map<String, Integer> getBestPrices(List<PriceGroup> priceGroups, List<Price> prices) 
{
    Map<String, Integer> bestPrice = new HashMap<String, Integer>(); 
    for (PriceGroup priceGroup : priceGroups) {
        if (bestPrice.get(priceGroup.priceGroup) == null) {
            bestPrice.put(priceGroup.priceGroup, 10000000);
        }

        for (Price price : prices) {
            if (price.priceName.equals(priceGroup.priceName)) {
                bestPrice.put(priceGroup.priceGroup, Math.min(price.price, bestPrice.get(priceGroup.priceGroup)));
            }
        }
    }

    return bestPrice;
}

Для заданных данных моя функция должна вернуть карту с:

F1 => 100
O1 => 250

Ответы [ 3 ]

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

Во-первых: я думаю, @ Руслан ответ, что вы должны использовать.

Но вы упомянули, что возвращаемое значение должно быть Map<String, Integer> и что строка должна быть F1 вместо Friends. Поэтому я попытался получить все это за один раз и получил эту мерзость функции:

public static Map<String, Integer> getBestPricesV2(List<PriceGroup> priceGroups, List<Price> prices) {
    final String unknownPriceName = "Unkown price name";

    return prices.stream().collect(Collectors.groupingBy(
            // map the price name the priceGroup name
            price -> priceGroups.stream()
                    .filter(priceGroup -> priceGroup.getPriceName().equals(price.getPriceName()))
                    .findFirst()
                    .map(PriceGroup::getPriceGroup)
                    .orElse(unknownPriceName), 
            Collectors.minBy(Comparator.comparing(Price::getPrice))))
        .entrySet()
        .stream()
        // extract the Optional<Price> to the price value
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().orElse(new Price(unknownPriceName, -1)).getPrice()));
}
2 голосов
/ 07 марта 2019

Вы можете просто избежать использования класса соединения для этой операции и выполнить его как:

public Map<String, Integer> getBestPrices(List<PriceGroup> priceGroups, List<Price> prices) {
// create a map of priceName to minimum price value using list of prices
    Map<String, Integer> minPrice = prices.stream()
            .collect(Collectors.groupingBy(Price::getPriceName,
                    Collectors.reducing(0, Price::getPrice,
                            BinaryOperator.minBy(Comparator.naturalOrder()))));

// use the map above to fill in the best prices map with values present or default
    return priceGroups.stream()
            .collect(Collectors.toMap(PriceGroup::getPriceGroup,
                    priceGroup ->
                            minPrice.getOrDefault(priceGroup.getPriceName(), 10000000)));
}
2 голосов
/ 07 марта 2019

Чтобы объединить 2 списка, вы можете создать выделенный объект:

class Joined {
    String priceGroup;
    String priceName;
    Integer price;
    ...

Затем, используя flatMap, вы можете присоединить priceGroups к prices в priceName поле и сгруппировать по priceGroup:

Map<String, Optional<Joined>> map = priceGroups.stream()
        .flatMap(group -> prices.stream()
                .filter(p -> p.getPriceName().equals(group.getPriceName()))
                .map(p -> new Joined(group.getPriceGroup(), group.getPriceName(), p.getPrice())))
        .collect(groupingBy(Joined::getPriceGroup, minBy(Comparator.comparing(Joined::getPrice))));

Теперь, получая значения из карты, вы можете распечатать ожидаемый результат:

for (Optional<Joined> value : map.values()) {
        value.ifPresent(joined -> System.out.println(joined.getPriceName() + " " + joined.getPrice()));
    }

// O1 250
// F1 100
...