Получение данных из карты внутри карты с потоками - PullRequest
2 голосов
/ 20 июня 2020

У меня проблема с получением данных из карты, вложенной в другую карту.

private Map<Customer, Map<Item,Integer>> orders;

Я генерирую эту карту из JSON, добавляю клиента, если его нет в списке с элементами и их количество. Если Клиент уже находится на карте, то ключевой элемент на второй карте обновляется, а если ключ уже был там, то обновляется целое число, которое представляет собой количество элементов. Классы Customer и Items не связаны. Я имею в виду, что у Class Customer нет поля Items, а у предметов класса Items нет поля Customer.

public class Customer {

    private String name;
    private String surname;
    private Integer age;
    private BigDecimal money;
}


public class Item {

    private String name;
    private String category;
    private BigDecimal price;
}

Используя потоки, я хочу получить, например, Customer, который заплатил больше всего для элементов, но у меня проблема с получением этих данных с карты, со списком это было не так сложно, но теперь я не могу это понять.

Хорошо, я понял что-то вроде этого, и, похоже, работает, но я уверен, что это можно упростить.

Customer key = customersMap.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey,
                        e -> e.getValue()
                                .entrySet()
                                .stream()
                                .map(o -> o.getKey().getPrice().multiply(BigDecimal.valueOf(o.getValue())))
                                .collect(Collectors.toList())))
                .entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, t -> t.getValue().stream().reduce(BigDecimal.ZERO, BigDecimal::add)))
                .entrySet()
                .stream()
                .max(Map.Entry.comparingByValue())
                .orElseThrow()
                .getKey();

Ваш ответ Наман был очень полезен, так что, возможно, вы дадите мне совет по этому поводу. Вот как я получаю его от JSON.

JsonConverterCustomer jsonConverterCustomer = new JsonConverterCustomer(FILENAME3);
List<Order> orders = jsonConverterCustomer.fromJson().orElseThrow();
 

Map<Customer, Map<Item, Integer>> customersMap = new HashMap<>();

        

for (Order order : orders) {
            if (!customersMap.containsKey(order.getCustomer())) {
                addNewCustomer(customersMap, order);
            } else {
                for (Product product : order.getItems()) {
                    if (!customersMap.get(order.getCustomer()).containsKey(items)) {
                        addNewCustomerItem(item, customersMap.get(order.getCustomer()));
                    } else {
                        updateCustomerItem(customersMap, order, item);
                    }
                }
            }
        }


private static void updateCustomerProduct(Map<Customer, Map<Item, Integer>> customersMap, Order order, Item item) {
        customersMap.get(order.getCustomer())
                .replace(item,
                        customersMap.get(order.getCustomer()).get(item),
                        customersMap.get(order.getCustomer()).get(item) + 1);
    }

    private static void addNewCustomerItem(Item item, Map<Item, Integer> itemIntegerMap) {
        itemIntegerMap.put(item, 1);
    }

    private static void addNewCustomer(Map<Customer, Map<Item, Integer>> customersMap, Order order) {
        Map<Item, Integer> temp = new HashMap<>();
        addNewCustomerItem(order.getItems().get(0), temp);
        customersMap.put(order.getCustomer(), temp);
    }

Класс заказа - это класс, который помогает мне получать данные из JSON. Это простой класс с клиентом в качестве поля и списком как поле. Как видите, я получаю список заказов и на его основе создаю эту карту. Можно сделать более функциональным? Используете потоки? Я пытался сделать, но не знал, как; /

1 Ответ

1 голос
/ 20 июня 2020

Есть два возможных способа сделать его более удобным для сопровождения / чтения, как указал Джейсон, и в то же время упростить лог c выполненный.

  • Один, вы можете избавиться от одного из этапы в конвейере и объединение map и reduce в один конвейер.
  • Другой вариант - абстрагирование для каждого клиента вычисления общей суммы, уплаченной ими.

Таким образом, абстракция будет выглядеть следующим образом и работать с внутренними картами для ваших входных данных:

private BigDecimal totalPurchaseByCustomer(Map<Item, Integer> customerOrders) {
    return customerOrders.entrySet()
            .stream()
            .map(o -> o.getKey().getPrice().multiply(BigDecimal.valueOf(o.getValue())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
}

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

private Customer maxPayingCustomer(Map<Customer, Map<Item, Integer>> customersMap) {
    Map<Customer, BigDecimal> customerPayments = customersMap.entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getKey,
                    e -> totalPurchaseByCustomer(e.getValue())));
    return customerPayments.entrySet()
            .stream()
            .max(Map.Entry.comparingByValue())
            .map(Map.Entry::getKey)
            .orElseThrow();
}
...