Amit
Amit

Reputation: 463

Convert List of List of Elements to Map < K, List<V>>

I have following structure.

Request {
    List<Param> paramList;
    Date startDate;
    Date endDate;
}

Param {
    String paramId;
    List<DataParam> dataParams; 
}

DataParam {
    String dataId;
    List<String> values;
}

CustomRequest {
    List<NewDataParam> newDataParam;
    Date startDate;
    Date endDate;
}

NewDataParam {
 String paramId;
 String dataId;
----
}

Iterating over the request with paramList, I want to make map of paramId and generated request like Map<String, List<CustomRequest>>.

With following code, I got Map<String, List<List<CustomRequest>>>. Can someone suggest how to get Map<String, List<CustomRequest>>.

request.paramList().stream()
    .collect(Collectors.groupingBy(Param::getParamId, 
        Collectors.mapping(i -> i.dataParams().stream()
           .map(r -> customBuildMethod(i.dataParamId(), r, request)).collect(Collectors.toList()), Collectors.toList())));

private CustomRequest customBuildMethod(String paramId ...) {
    CustomRequest customRequest = new CustomRequest();
    //mapping
    return customRequest;
}

Upvotes: 3

Views: 264

Answers (2)

Eran
Eran

Reputation: 393821

If you can use Java 9, you can achieve your goal with Collectors.flatMapping:

request.paramList()
       .stream()
       .collect(Collectors.groupingBy(Param::getParamId, 
                                      Collectors.flatMapping(i -> i.dataParams().stream().map(r -> customBuildMethod(i.dataParamId(), r, request))), 
                                                            Collectors.toList())));

In Java 8, you can do the following:

Map<String, List<CustomRequest>> result = 
    request.paramList()
          .stream()
          .collect(Collectors.groupingBy(Param::getParamId, 
                                         Collector.of(ArrayList::new, 
                                                     (l,i)->l.addAll(i.dataParams().stream().map(r -> customBuildMethod(i.dataParamId(), r, request)).collect(Collectors.toList())),
                                                     (l1,l2)->{l1.addAll(l2);return l1;})));

Upvotes: 5

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

Instead of Collectors.mapping() you can use Collectors.collectingAndThen() where in finisher function you can do a flatMap using list of data params. Something like that:

Map<String, List<CustomRequest>> map = params.stream()
        .collect(Collectors.groupingBy(
                Param::getParamId, Collectors.collectingAndThen(Collectors.toList(), it -> it.stream()
                        .flatMap(i -> i.getDataParams().stream())
                        .map(r -> new CustomRequest(Arrays.asList(new NewDataParam()), new Date(), new Date()))
                        .collect(Collectors.toList())
                ))
        );

System.out.println(map);

Full example I used for tests:

import java.util.*;
import java.util.stream.Collectors;

public class ListOfListsToMapOfStringAndListExample {

    public static void main(String[] args) {

        final List<Param> params = Arrays.asList(
                new Param("1", Arrays.asList(new DataParam("2", Arrays.asList("a", "b")))),
                new Param("3", Arrays.asList(new DataParam("4", Arrays.asList("c", "d")))),
                new Param("5", Arrays.asList(new DataParam("6", Arrays.asList("e", "f"))))
        );

        Map<String, List<CustomRequest>> map = params.stream()
                .collect(Collectors.groupingBy(
                        Param::getParamId, Collectors.collectingAndThen(Collectors.toList(), it -> it.stream()
                                .flatMap(i -> i.getDataParams().stream())
                                .map(r -> new CustomRequest(Arrays.asList(new NewDataParam()), new Date(), new Date()))
                                .collect(Collectors.toList())
                        ))
                );

        System.out.println(map);
    }


    static class Request {
        List<Param> paramList;
        Date startDate;
        Date endDate;

        public Request(List<Param> paramList, Date startDate, Date endDate) {
            this.paramList = paramList;
            this.startDate = startDate;
            this.endDate = endDate;
        }
    }

    static class Param {
        String paramId;
        List<DataParam> dataParams;

        public Param(String paramId, List<DataParam> dataParams) {
            this.paramId = paramId;
            this.dataParams = dataParams;
        }

        public String getParamId() {
            return paramId;
        }

        public List<DataParam> getDataParams() {
            return dataParams;
        }
    }

    static class DataParam {
        String dataId;
        List<String> values;

        public DataParam(String dataId, List<String> values) {
            this.dataId = dataId;
            this.values = values;
        }

        public String getDataId() {
            return dataId;
        }

        public List<String> getValues() {
            return values;
        }
    }

    static class CustomRequest {
        List<NewDataParam> newDataParam;
        Date startDate;
        Date endDate;

        public CustomRequest(List<NewDataParam> newDataParam, Date startDate, Date endDate) {
            this.newDataParam = newDataParam;
            this.startDate = startDate;
            this.endDate = endDate;
        }

        public List<NewDataParam> getNewDataParam() {
            return newDataParam;
        }

        public Date getStartDate() {
            return startDate;
        }

        public Date getEndDate() {
            return endDate;
        }

        @Override
        public String toString() {
            return "CustomRequest{" +
                    "newDataParam=" + newDataParam +
                    ", startDate=" + startDate +
                    ", endDate=" + endDate +
                    '}';
        }
    }

    static class NewDataParam {

        @Override
        public String toString() {
            return "NewDataParam{}";
        }
    }
}

Output:

{1=[CustomRequest{newDataParam=[NewDataParam{}], startDate=Wed Feb 14 09:03:21 CET 2018, endDate=Wed Feb 14 09:03:21 CET 2018}], 3=[CustomRequest{newDataParam=[NewDataParam{}], startDate=Wed Feb 14 09:03:21 CET 2018, endDate=Wed Feb 14 09:03:21 CET 2018}], 5=[CustomRequest{newDataParam=[NewDataParam{}], startDate=Wed Feb 14 09:03:21 CET 2018, endDate=Wed Feb 14 09:03:21 CET 2018}]}

Update:

In case of passing param.paramId to customBuildMethod you can even get rid of flatMap() onparam.dataParams and map your initial param object to a CustomRequest. Something like:

Map<String, List<CustomRequest>> map = params.stream()
        .collect(Collectors.groupingBy(
                Param::getParamId, Collectors.collectingAndThen(Collectors.toList(), it -> it.stream()
                        .map(param -> customBuildMethod(param.paramId, param.dataParams))
                        .collect(Collectors.toList())
                ))
        );

In this example I pass param.paramId and param.dataParams to this helper method which creates CustomRequest object. It can use a list of data params to create a new list of new data params objects:

private static CustomRequest customBuildMethod(String paramId, List<DataParam> params) {
    return new CustomRequest(
            params.stream().map(dataParam -> new NewDataParam()).collect(Collectors.toList()),
            new Date(),
            new Date()
    );
}

Or more directly according to your code:

Map<String, List<CustomRequest>> map = params.stream()
        .collect(Collectors.groupingBy(
                Param::getParamId, Collectors.collectingAndThen(Collectors.toList(), it -> it.stream()
                        .map(param -> customBuildMethod(param.paramId, r, request))
                        .collect(Collectors.toList())
                ))
        );

Upvotes: 4

Related Questions