Home > Software engineering >  Merge the data relating to each unique Object in a List and transform them into a List of another Ty
Merge the data relating to each unique Object in a List and transform them into a List of another Ty

Time:06-26

I have a list of Beans (List 1) and I want to convert it to another list (List 2).

I've managed to achieve that. But I have the impression it made it too complicated : pick out the id's loop stream again.

My question is : is it possible to make it simpler ?

Sample data - List1:

[Customer [id=1, name=jon, carType=fiat, color=black], 
 Customer [id=2, name=din, carType=ford, color=yellow], 
 Customer [id=1, name=jon, carType=benz, color=white], 
 Customer [id=1, name=jon, carType=volvo, color=red], 
 Customer [id=3, name=fin, carType=volvo, color=black], 
 Customer [id=3, name=fin, carType=fiat, color=green], 
 Customer [id=4, name=lara, carType=bmw, color=red], 
 Customer [id=5, name=tina, carType=toyota, color=white], 
 Customer [id=5, name=tina, carType=fiat, color=yelow], 
 Customer [id=6, name=bogi, carType=benz, color=black]]

Sample data - List2:

ImprovedCustomer [id=1, name=jon, cars=[Car [carType=fiat, color=black], Car [carType=benz, color=white], Car [carType=volvo, color=red]]]
ImprovedCustomer [id=2, name=din, cars=[Car [carType=ford, color=yellow]]]
ImprovedCustomer [id=3, name=fin, cars=[Car [carType=volvo, color=black], Car [carType=fiat, color=green]]]
ImprovedCustomer [id=4, name=lara, cars=[Car [carType=bmw, color=red]]]
ImprovedCustomer [id=5, name=tina, cars=[Car [carType=toyota, color=white], Car [carType=fiat, color=yelow]]]
ImprovedCustomer [id=6, name=bogi, cars=[Car [carType=benz, color=black]]]

My code:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class App {

    public static void main(String[] args) {
        List<Customer> list = new ArrayList<>();
        list.add(new Customer(1, "jon", "fiat", "black"));
        list.add(new Customer(2, "din", "ford", "yellow"));
        list.add(new Customer(1, "jon", "benz", "white"));
        list.add(new Customer(1, "jon", "volvo", "red"));
        list.add(new Customer(3, "fin", "volvo", "black"));
        list.add(new Customer(3, "fin", "fiat", "green"));
        list.add(new Customer(4, "lara", "bmw", "red"));
        list.add(new Customer(5, "tina", "toyota", "white"));
        list.add(new Customer(5, "tina", "fiat", "yelow"));
        list.add(new Customer(6, "bogi", "benz", "black"));

        List<Integer> ids = list.stream().map(Customer::getId).distinct().collect(Collectors.toList());

        List<ImprovedCustomer> ImprovedCustomers = new ArrayList<>();

        for (int id : ids) {
            ImprovedCustomer improvedCustomer = new ImprovedCustomer(id);
            List<Car> collect = list.stream().filter(a -> a.getId().equals(id)).map(a -> {
                if (improvedCustomer.getName() == null) {
                    improvedCustomer.setName(a.getName());
                }
                return new Car(a.getCarType(), a.getColor());
            }).collect(Collectors.toList());

            improvedCustomer.setCars(collect);
            ImprovedCustomers.add(improvedCustomer);
        }
    }
}

class Customer {

    private Integer id;
    private String name;
    private String carType;
    private String color;

    public Customer(Integer id, String name, String carType, String color) {
        super();
        this.id = id;
        this.name = name;
        this.carType = carType;
        this.color = color;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCarType() {
        return carType;
    }

    public void setCarType(String carType) {
        this.carType = carType;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

class Car {

    private String carType;
    private String color;

    public Car(String carType, String color) {
        super();
        this.carType = carType;
        this.color = color;
    }

    public String getCarType() {
        return carType;
    }

    public void setCarType(String carType) {
        this.carType = carType;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

class ImprovedCustomer {

    private Integer id;
    private String name;
    private List<Car> cars;

    public ImprovedCustomer() {
        super();
    }

    public ImprovedCustomer(Integer id) {
        super();
        this.id = id;
    }

    public ImprovedCustomer(Integer id, String name, List<Car> cars) {
        super();
        this.id = id;
        this.name = name;
        this.cars = cars;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Car> getCars() {
        return cars;
    }

    public void setCars(List<Car> cars) {
        this.cars = cars;
    }
}

CodePudding user response:

Your current solution performs an iteration over the list of customers for every unique id.

As @Johannes Kuhn has pointed out in the comments, you can eliminate redundant iterations by making use of the collector groupingBy(classifier, downstream). With this collector, we can map each customer with unique id to a list of cars in a single iteration.

And then construct a list of ImprovedCustomer objects based on every entry of the map generated by groupingBy().

Note: that in order to use this approach, either Customer class should implement equals/hashCode contract based on the id and name properties, or we need to use an auxiliary record (or class) that would wrap a Customer object. For simplicity, I'll assume that we can rely on the Customer class equals/hashCode implementations.

So, in the code below as classifier function I've applied Function.identity() (i.e. a key would be a customer itself) and as downstream collector a combination of collectors mapping() and toList(), which transforms every customer into a Car object, and collect the cars mapped to the same customer into a list.

public static void main(String[] args) {
    List<Customer> customers = // initializing the list of customers

    List<ImprovedCustomer> improvedCustomers = customers.stream()
        .collect(Collectors.groupingBy(
            Function.identity(),
            Collectors.mapping(cust -> new Car(cust.getCarType(), cust.getColor()),
                Collectors.toList())
        ))
        .entrySet().stream()
        .map(entry -> new ImprovedCustomer(entry.getKey().getId(), entry.getKey().getName(), entry.getValue()))
        .collect(Collectors.toList());
}
  • Related