'Is there a neat way of converting lists into maps with the keys set to the elements of original collection and values set to an enum in java
Java newbie... I have a dictionary like so:
electronictypeModelsMap =
{
"laptops" => ["macbook", "thinkpad", "yoga"],
"desktops" => ["macmini", "imac", "otherDesktop"],
"smartphones" => ["iphone", "galaxy5", "oneplus"]
}
There will only ever be a small number of keys like the 3 above but there will be a few hundred elements in each value (list)....
I want to be able to query it the other way around so :
dictionary.get("macbook") would return "laptops"
So I guess I want to convert into a new map with each element of each list as a key with the corresponding value set to the original key. like this:
{
"macbook" => "laptops",
"thinkpad" => "laptops",
"yoga"=> "laptops",
"macmini" => "desktops",
"imac" => "desktops",
"otherDesktop" => "desktops",
"iphone" => "smartphones",
"galaxy5" => "smartphones",
"oneplus" => "smartphones",
}
Is there a neat way of doing this in Java 11?
In ruby you could do something like this
newMap = Hash.new
electronictypeModelsMap.each do |k,v|
v.each do |value|
newMap[value] = k
end
end
Solution 1:[1]
Assuming you have a map like below:
Map<String,List<String>> electronictypeModelsMap =
Map.of("laptops", List.of("macbook", "thinkpad", "yoga"),
"desktops", List.of("macmini", "imac", "otherDesktop"),
"smartphones", List.of("iphone", "galaxy5", "oneplus"));
the simplest solution could be to use a nested forEach
Map<String,String> reMapped = new HashMap<>();
electronictypeModelsMap.forEach((key,list) -> {
list.forEach(item -> {
reMapped.put(item, key);
});
});
Solution 2:[2]
If you're certain that every element in each list of values will be unique (i.e. distinct in the scope of all map) you can take the following approach:
- Create a stream over the entry set;
- Flatten the entries using
flatMap()by creating a new entry based on each element of the list and a key to which the current list was mapped, so that a value becomes a key and vice versa. - Collect the data into a map using
Collectors.toMap().
Map<String, List<String>> source =
Map.of("laptops", List.of("macbook", "thinkpad", "yoga"),
"desktops", List.of("macmini", "imac", "otherDesktop"),
"smartphones", List.of("iphone", "galaxy5", "oneplus"));
Map<String, String> result =
source.entrySet().stream()
.flatMap(entry -> entry.getValue().stream().map(element ->
Map.entry(element, entry.getKey())))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
result.forEach((k, v) -> System.out.println(k + " => " + v));
Output
galaxy5 => smartphones
macmini => desktops
macbook => laptops
etc.
Note that if in the initial map will have at least one element (like "macbook") that has been mapped to more than one category, the collector listed above will be unable to resolve duplicates, and it'll cause an IllegalStateException. As a remedy, you need to define how to override the value by providing a mergeFunction as the third argument of toMap(), or utilize the collector Collectors.groupingBy() instead to preserve all the values mapped to the same key.
Solution 3:[3]
No need to produce another map. You can query the existing map, to find a key whose value contains the target string.
Streams are useful here to make that query.
Make some example data.
Map < String, Set < String > > map =
Map.of(
"laptops" , Set.of( "macbook" , "thinkpad" , "yoga" ) ,
"desktops" , Set.of( "macmini" , "imac" , "otherDesktop" ) ,
"smartphones" , Set.of( "iphone" , "galaxy5" , "oneplus" )
);
You said:
dictionary.get("macbook") would return "laptops"
We can get all the entries of a map. Each entry is a key-value pairing, our string leading to a set of strings.
Make a stream of the entries of the map.
Filter the entries, looking for any whose value (the set of strings) contains our target, macbook. Stop looking after finding a match.
The result of this in an Optional entry. We need Optional here because there may be none found, we may have no hits.
In this case of your example, we know we have a hit, so proceed. We know the found entry’s value is a set containing our target. So extract the key, and report.
Optional < Map.Entry < String, Set < String > > > entry =
map
.entrySet()
.stream()
.filter( ( Map.Entry < String, Set < String > > stringSetEntry ) -> stringSetEntry.getValue().contains( "macbook" ) )
.findAny();
String result = entry.get().getKey();
See this code run live at IdeOne.com.
result = laptops
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Eritrean |
| Solution 2 | |
| Solution 3 |
