Home > Enterprise >  Is there a neat way of converting lists into maps with the keys set to the elements of original coll
Is there a neat way of converting lists into maps with the keys set to the elements of original coll

Time:05-07

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 

CodePudding user response:

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

CodePudding user response:

Streams may be useful here.

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();
System.out.println( "result = "   result );

See this code run live at IdeOne.com.

result = laptops

CodePudding user response:

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.

  • Related