Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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]]

like image 903
tryingToLearn Avatar asked Nov 01 '19 07:11

tryingToLearn


People also ask

How do you convert a map key to a list?

We can convert Map keys to a List of Values by passing a collection of map values generated by map. values() method to ArrayList constructor parameter.

What is the difference between MAP and flatMap in Java streams?

Both map and flatMap can be applied to a Stream<T> and they both return a Stream<R> . The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.

How to convert map keys to list of values in Java?

We can convert Map keys to List of Values by passing collection of map values generated by map.values () method to ArrayList Constructor Parameter. You can also use Stream API to convert map keys and values to respective lists. The stream () method returns a stream of the keys from the Set of the map keys returned by Map.keySet ().

How do I convert a map to a list?

The values () method returns a Collection of the values in the map, and naturally, since a List implements Collection, the conversion is as easy as passing it in the List 's constructor: We'll steam () the keys and values of a Map, and then collect () them into a List:

What is the difference between a map and a list?

A Map is an object that maps keys to values or is a collection of attribute-value pairs. The list is an ordered collection of objects and the List can contain duplicate values. The Map has two values (a key and value), while a List only has one value (an element). So we can generate two lists as listed: List of keys from a Map.

How do I flatten a list to an array?

Or flatten all sub-lists using Stream#flatMap and then just collect it as array with Stream#toArray. Your code looks a bit hard to read, especially the size-computation. Here's a way to do it with Java 8 features (but without the overhead of streams): List<String> result = new ArrayList<> (); mapOfWordsArrays.values ().forEach (result::addAll);


4 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]]
like image 141
Eran Avatar answered Oct 12 '22 23:10

Eran


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);


like image 32
Vinay Prajapati Avatar answered Oct 12 '22 23:10

Vinay Prajapati


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]
like image 26
deHaar Avatar answered Oct 13 '22 00:10

deHaar


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

like image 45
Donald Raab Avatar answered Oct 13 '22 01:10

Donald Raab