Home » Java » Most efficient way to convert/flatten entire map to list (keys & values together , not separately)

Most efficient way to convert/flatten entire map to list (keys & values together , not separately)

Posted by: admin December 28, 2021 Leave a comment

Questions:

I need to convert following:

Map<Long,Map<String,String>> mapOfMaps

to

List<List<String>> listOflists

where keys of outer map (mapOfMaps) are redundant (for this operation). So basically , I can just use mapOfMaps.values().stream() to start with.

And for each map object e.g.:

{“apple”:”1″,”orange”:”2″}

I need to convert it to a list :

{“apple”,”1″,”orange”,”2″}

What is the most efficient way to do this?

Complete example:

{
“1L” : { “key1” : “value1” , “key2” : “value2” } ,
“2L” : { “key3” : “value3” , “key4” : “value4” }
}

Expected:

[[key1, value1, key2, value2], [key3, value3, key4, value4]]
Answers:

Something like this:

List<List<String>> listOflists =
    mapOfMaps.values()
             .stream()
             .map(m -> m.entrySet()
                        .stream()
                        .flatMap(e->Stream.of(e.getKey(),e.getValue()))
                        .collect(Collectors.toList()))
             .collect(Collectors.toList());

For each inner Map, you stream over the entrySet(), and create a stream of all the keys and values, which you collect into a List.

For example, if you initialize the Map with:

Map<Long,Map<String,String>> mapOfMaps = new HashMap<>();
mapOfMaps.put(1L,new HashMap());
mapOfMaps.put(2L,new HashMap());
mapOfMaps.get(1L).put("key1","value1");
mapOfMaps.get(1L).put("key2","value2");
mapOfMaps.get(2L).put("key3","value3");
mapOfMaps.get(2L).put("key4","value4");

You’ll get the following List:

[[key1, value1, key2, value2], [key3, value3, key4, value4]]

###

Below is my version of solution. You can iterate over entry and add values to desired list accordingly.

        List<List<String>> list = map.
                values()
                .stream()
                .map(value -> {
                    List<String> list1 = new ArrayList<>();
                    for (Map.Entry<String, String> entry : value.entrySet()) {
                        list1.add(entry.getKey());
                        list1.add(entry.getValue());
                    }
                    return list1;
                })
                .collect(Collectors.toList());

Test Input:


        Map<Long, Map<String, String>> map = new HashMap<>();

        Map<String, String> submap1 = new HashMap<>();
        submap1.put("test", "test2");

        Map<String, String> submap2 = new HashMap<>();
        submap2.put("test6", "6");

        map.put(1l, submap1);
        map.put(2l, submap2);


###

Here’s a solution, which is a little less cool than nested lambdas, but a little more readable:

public static void main(String[] args) {
    // create a result list
    List<List<String>> resultList = new ArrayList<>();

    // create some try-out values
    // a source map
    Map<Long,Map<String,String>> mapOfMaps = new HashMap<>();
    // some sub maps
    Map<String, String> subMapOne = new HashMap<>();
    subMapOne.put("One", "1");
    subMapOne.put("Two", "2");
    subMapOne.put("Three", "3");

    Map<String, String> subMapTwo = new HashMap<>();
    subMapTwo.put("Two", "2");

    Map<String, String> subMapThree = new HashMap<>();
    subMapThree.put("One", "1");
    subMapThree.put("Three", "3");

    mapOfMaps.put(1l, subMapOne);
    mapOfMaps.put(2l, subMapTwo);
    mapOfMaps.put(3L, subMapThree);

    // just nest two forEach-calls
    mapOfMaps.forEach((key, value) -> {
        // create a new list for each submap
        List<String> subList = new ArrayList<>();
        value.forEach((subKey, subValue) -> {
            // and add each entry of the submap to it
            subList.add(subKey);
            subList.add(subValue);
        });
        // finally add the list to the result list
        resultList.add(subList);
    });

    resultList.forEach(System.out::println);
}

The output is

[One, 1, Two, 2, Three, 3]
[Two, 2]
[One, 1, Three, 3]

###

If you’re open to using a third-party library the following will work using Eclipse Collections:

@Test
public void mapOfMapsToListOfLists()
{
    MutableMap<Long, MutableSortedMap<String, String>> map = Maps.mutable.with(
            1L, SortedMaps.mutable.with("key1", "value1", "key2", "value2"),
            2L, SortedMaps.mutable.with("key3", "value3", "key4", "value4"));

    MutableList<MutableList<String>> listOfLists = map.valuesView()
            .collect(innerMap -> innerMap.keyValuesView()
                    .flatCollect(this::pairToList).toList())
            .toList();

    List<List<String>> expected = Lists.mutable.with(
            Lists.mutable.with("key1", "value1", "key2", "value2"),
            Lists.mutable.with("key3", "value3", "key4", "value4"));
    Assert.assertEquals(expected, listOfLists);
}

public ImmutableList<String> pairToList(Pair<String, String> pair)
{
    return Lists.immutable.with(pair.getOne(), pair.getTwo());
}

I initialized the inner maps as SortedMaps in order to guarantee the order of the keys in the call to Assert.assertEquals in the test. The interface types I used above are from Eclipse Collections and extend the JDK interface types (e.g. MutableMap extends Map, MutableList extends List), but you can also use JDK types with static utility as follows:

@Test
public void jdkMapOfMapsToListOfLists()
{
    Map<Long, Map<String, String>> map = Maps.mutable.with(
            1L, SortedMaps.mutable.with("key1", "value1", "key2", "value2"),
            2L, SortedMaps.mutable.with("key3", "value3", "key4", "value4"));

    List<List<String>> listOfLists = MapIterate.collect(map,
            innerMap -> Iterate.flatCollect(
                    innerMap.entrySet(),
                    this::entryToList,
                    new ArrayList<>()));

    List<List<String>> expected = Arrays.asList(
            Arrays.asList("key1", "value1", "key2", "value2"),
            Arrays.asList("key3", "value3", "key4", "value4"));
    Assert.assertEquals(expected, listOfLists);
}

public List<String> entryToList(Map.Entry<String, String> entry)
{
    return Arrays.asList(entry.getKey(), entry.getValue());
}

Note: I am a committer for Eclipse Collections