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]]
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.
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.
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 ().
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:
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.
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);
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 SortedMap
s 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With