Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java-8: stream or simpler solution?

I have two models, a List<ModelA> and I want to convert it to a List<ModelB>. Here are my models:

class ModelA {

    private Long id;
    private String name;
    private Integer value;

    public ModelA(Long id, String name, Integer value) {
        this.id = id;
        this.name = name;
        this.value = value;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Integer getValue() {
        return value;
    }
}

class ModelB {
    private Long id;
    private Map<String, Integer> valuesByName;

    public ModelB(Long id, Map<String, Integer> valuesByName) {
        this.id = id;
        this.valuesByName = valuesByName;
    }

    public Long getId() {
        return id;
    }

    public Map<String, Integer> getValuesByName() {
        return valuesByName;
    }
}

Actual solution:

public static List<ModelB> convert(List<ModelA> models) {
        List<ModelB> toReturn = new ArrayList<>();
        Map<Long, Map<String, Integer>> helper = new HashMap<>();
        models.forEach(modelA -> {
            helper.computeIfAbsent(modelA.getId(), value -> new HashMap<>())
                    .computeIfAbsent(modelA.getName(), value -> modelA.getValue());
        });
        helper.forEach((id, valuesByName) -> toReturn.add(new ModelB(id,valuesByName)));
        return toReturn;
    }

But I think there is a simpler solution, do you have any idea how can I do it in a single stream, or simplify it somehow?

EDIT: I want to clarify that I cannot use java9 and I need to group them by Id-s then by Name. If in ModelB I have 4 elements with the same id I don't want new instances of ModelA.

like image 966
Sunflame Avatar asked Mar 28 '18 10:03

Sunflame


1 Answers

I have combined both operations, but still constructs the intermediate map as you need to group all name, value pairs for a given id.

models.stream()
        .collect(Collectors.groupingBy(model -> model.getId(), //ModelA::getId - Using method reference
                Collectors.toMap(model -> model.getName(), model -> model.getValue(), (map1, map2) -> map1)))
        .entrySet()
        .stream()
        .map(entry -> new ModelB(entry.getKey(), entry.getValue()))
        .collect(Collectors.toList());

EDIT:

I missed (map1, map2) -> map1 in the initial answer. It is needed to avoid overwriting the already existing value for a id, name(the equivalent of your second computeIfAbsent in your code) You need to choose one of them (or mege them), as by default it throws IllegalStateException when it finds a duplicate key.

like image 55
user7 Avatar answered Oct 11 '22 20:10

user7