Search code examples
javahashmaptreemaplinkedhashmap

Java: copy values by matching similar keys from LinkedHashMap


I have a LinkedHashMap and would like to do the following:

  1. loop through the HashMap and check if the string in the Key contains 'Group' word in it
  2. create String[] of all the Values matching that Key string

For example, if I have a LinkedHashMap like the below

HashMap<String, String> nameNum = new LinkedHashMap<String, String>();
nameNum.put("row 1", "data 1");
nameNum.put("Group_Colour_R", "Red");
nameNum.put("Group_Colour_B", "Blue");
nameNum.put("row 4", "data 4");
nameNum.put("row 5", "data 5");
nameNum.put("Group_Shape_R", "Rectangle");
nameNum.put("Group_Shape_T", "Triangle");
nameNum.put("Group_Shape_C", "Circle");
nameNum.put("row 9", "data 9");
nameNum.put("row 10", "data 10");
nameNum.put("row 11", "data 11");

After looping through the above HashMap, I should have 2 String[]

String[] colour = {"Red", "Blue"}; 
String[] shape = {"Rectangle", "Triangle", "Circle"};

I'm just not sure how to achieve it.

There is something related to what I want and found it here: Partial search in HashMap

Based on the above I implemented it like below but, as you would see, it runs the same values multiple times. I'm not sure when and how to break the loop so that it only gives the values once:

import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.StringUtils;

public class MainClass {
    public static void main(String[] args) {
        SortedMap<String, String> nameNum = new TreeMap<String, String>();
        nameNum.put("row 1", "data 1");
        nameNum.put("Group_Colour_R", "Red");
        nameNum.put("Group_Colour_B", "Blue");
        nameNum.put("row 4", "data 4");
        nameNum.put("row 5", "data 5");
        nameNum.put("Group_Shape_R", "Rectangle");
        nameNum.put("Group_Shape_T", "Triangle");
        nameNum.put("Group_Shape_C", "Circle");
        nameNum.put("row 9", "data 9");
        nameNum.put("row 10", "data 10");
        nameNum.put("row 11", "data 11");


        for (Map.Entry<String, String> entry : nameNum.entrySet()) {
            String prefix = entry.getKey();
            if (prefix.contains("Group")) {
                prefix = prefix.substring(0, StringUtils.lastIndexOf(prefix, "_"));
                System.out.println("******");
                for (Map.Entry<String, String> entry1 : filterPrefix(nameNum, prefix).entrySet()) {
                    System.out.println(entry1);
                }
                System.out.println("******");
            }
        }
    }

    public static <V> SortedMap<String, V> filterPrefix(SortedMap<String,V> baseMap, String prefix) {
        if(prefix.length() > 0) {
            char nextLetter = (char) (prefix.charAt(prefix.length() -1) + 1);
            String end = prefix.substring(0, prefix.length()-1) + nextLetter;
            return baseMap.subMap(prefix, end);
        }
        return baseMap;
    }
}

Ideally, I'd like to use a LinkedHashMap because I want to preserve the order. I'd highly appreciate any pointers/code on how to implement something similar. Any code snippet would be highly appreciated. Thanks in advance.


Solution

  • Perhaps Java 8 Stream API could be helpful here. You can try to group the relevant entries together using Collectors.groupingBy:

    Map<String,List<String>> groups =
        nameNum.entrySet()
               .stream()
               .filter(entry -> entry.getKey().contains("Group"))
               .collect(Collectors.groupingBy(entry -> entry.getKey().substring(0, entry.getKey().lastIndexOf("_")),
                                              Collectors.mapping(Map.Entry::getValue,
                                                                 Collectors.toList()));
    

    This produces a Map the contains:

    {Group_Shape=[Rectangle, Triangle, Circle], Group_Colour=[Red, Blue]}
    

    You can also force the output Map to be a LinkedHashMap if you prefer:

    Map<String,List<String>> groups =
        nameNum.entrySet()
               .stream()
               .filter(entry -> entry.getKey().contains("Group"))
               .collect(Collectors.groupingBy(entry -> entry.getKey().substring(0, entry.getKey().lastIndexOf("_")),                     
                                              LinkedHashMap::new, 
                                              Collectors.mapping(Map.Entry::getValue,
                                                                 Collectors.toList()));
    

    This would alter the iteration order of the output Map:

    {Group_Colour=[Red, Blue], Group_Shape=[Rectangle, Triangle, Circle]}