Home > Software engineering >  How to transform a List<Map<String, Object>> into Map<String, List<Object>>
How to transform a List<Map<String, Object>> into Map<String, List<Object>>

Time:10-12

I have a list with a map inside and I need to turn it into a map with a list inside.

I have a List <Map <String, Object >> and I need to change it to Map<String, List <Object<Object>>>.

, of course, by passing the data contained in it, matching the keys to the values

There are objects of the Date class in the Object class, this structure was used because it is the result returned by jdbcTemplate. Now I need to change that to have a date list for each unique String key.

I have a method that takes two columns from a database and returns List<Map<String, Object >>. One column is the username and the second column is the dates on which he was on vacation. I need a data structure that will allow me to match the key (userId) with a list of his vacation dates.

public List<Map<String, Object>> getHoliday(String teamLeaderId) {
    String query = "SELECT  H.userid, date FROM Holiday H INNER JOIN Contact C on H.userid = C.UserId INNER JOIN Team T on C.TeamId = T.team_id WHERE team_leader = ? AND isApproved = 0";
    return this.getJdbcTemplate().queryForList(query, teamLeaderId);
}

I'm using Java 8.

Is it possible to do?

CodePudding user response:

Plain for-loop

You can group entries having identical keys using Java 8 method computeIfAbsent():

List<Map<String, Object>> listOfMaps = // initilizing the list

Map<String, List<Object>> result = new HashMap<>();

for (Map<String, Object> map: listOfMaps) {
    for (Map.Entry<String, Object> entry: map.entrySet()) {
        result
            .computeIfAbsent(entry.getKey(), k -> new ArrayList<>())
            .add(entry.getValue());
    }
}

If you don't feel comfortable with Java 8 features than you can replace computeIfAbsent() with the lines shown below (note that used of computeIfAbsent() is preferred over putIfAbsent() because function would be evaluated lazily, meanwhile putIfAbsent() would create a new empty List with every call):

result.putIfAbsent(entry.getKey(), new ArrayList<>());
result.get(entry.getKey()).add(entry.getValue());

Stream API

You can address this problem using Stream API:

  • Flatten the entries of each Map using flatMap() operation;

  • Group the entries having the same key using collector groupingBy() and apply the combination of collectors mapping() and toList() as a downstream.

That's how it might look like:

List<Map<String, Object>> listOfMaps = // initilizing the list

Map<String, List<Object>> result = listOfMaps.stream()
    .flatMap(map -> map.entrySet().stream())
    .collect(Collectors.groupingBy(
        Map.Entry::getKey,
        Collectors.mapping(Map.Entry::getValue,
            Collectors.toList())
    ));

CodePudding user response:

Its possible to do so using Collectors.toMap to convert the List to Map , Here a small exemple to how to do it.

package org.example;

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

public class App 
{
    public static void main( String[] args )
    {
        ArrayList<Map<String, Object >> list =  new ArrayList <Map <String, Object >>(){{ // just to simulate the result
            for(int i = 0; i < 10; i  ){
                final Integer finalI = i   1;
                final Integer simulateID = (finalI % 3)   1;

                add(new HashMap<String, Object>(){{
                    Calendar cal = Calendar.getInstance();
                    cal.add(Calendar.DAY_OF_MONTH, finalI);
                    put("userid", simulateID.toString() );
                    put("date", cal.getTime());
                }});
            }
        }};

        Map<String, ArrayList<Object>> map = list
        .stream()
        .collect(
            Collectors.toMap(
                    e -> (String) e.get("userid"),
                    e -> new ArrayList<Object>(){{ add(e.get("date")); }},
                    (v1, v2) -> {
                        v1.addAll(v2);
                        return v1;
                    }
            )
        );

        System.out.println(map);
    }
}

But before using something like that maybe is better take a look in this article to see if it fit to your needs https://arnaudroger.github.io/blog/2017/06/13/jdbc-template-one-to-many.html

CodePudding user response:

Plain Java

public static <T> Map<String, List<T>> convert(List<Map<String, T>> items) {
    Map<String, List<T>> map = new HashMap<>();

    for (Map<String, T> item : items)
        for (Map.Entry<String, T> entry : item.entrySet())
            map.computeIfAbsent(entry.getKey(), key -> new ArrayList<>()).add(entry.getValue());

    return map;
}

Stream

public static <T> Map<String, List<T>> convert(List<Map<String, T>> items) {
    return items.stream()
                .flatMap(item -> item.entrySet().stream())
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                                               Collectors.mapping(Map.Entry::getValue,
                                                                  Collectors.toList())));
}
  • Related