Search code examples
javalistdictionaryhashmap

Java removing subset of a nested map containing list, then reversing map


I currently have some data in the form of a nested map like the following:

Map<String, Map<String, List<Integer>>> data = // intializing the Map
    
// data = {group1={grades=[a,b,c], age=[x,y,z]}, group={....}} and so on

What I want to do is essentially remove grades and then reverse the map, so it would look something like:

{x={group1}, y={group1, group3}, z={group2}, someAge={some list of groupN}}

I appreciate that the data may not make sense without context, but the problem remains the same. As it stands, I have a 3-nested for loops, but there must be a better way, since that would be very inefficient with a large dataset:

Map <Integer, List<String>> someMap = new HashMap<>();

for (var entry : data.entrySet()) {
    for (var info : entry.getValue().entrySet()) {
        if (info.getKey().equals("age")) {
            List ages = info.getValue();
            for (var age : ages) {
                // if age not in someMap, add age as key // e.g add x so someMap={x={},y={}}
                someMap.get(age).add(entry.getKey()) //e.g {x={entry key},y={}}
            }
        }
    }
}

Is there some kind of pre-processing I can do to remove grades which would cut the 2nd for loop in half at least, and remove the need to check if the key is 'age' each iteration.


Solution

  • You don't need the second nested for-loop where you're iterating over the entry sets of the inner Map searching for the key "age". Instead, you can retrieve a value that corresponds to this key from each inner Map.

    That's how your logic can be implemented using Java 8 methods Map.computeIfAbsent() and Map.forEach().

    As a precaution against the case when the key "age" is not present in the inner Map you can use Map.getOrDefault()

    Map<String, Map<String, List<Integer>>> data = // {group1={grades=[a,b,c], age=[x,y,z]}, group={....}} and so on
            
    Map<Integer, List<String>> groupsByAge = new HashMap<>();
    
    
    data.forEach((group, nestedMap) -> {
        nestedMap.getOrDefault("age", List.of()).forEach(age -> 
            groupsByAge.computeIfAbsent(age, k -> new ArrayList<>()).add(group)
        );
    });
    

    Note

    It's also worth to note that your data is not dynamic, i.e. every nested Map is expected to have Keys: "grades", "age", etc. Then it should be an object having properties grades and age. Because custom domain object allows to structure the data, utilize appropriate data types and manipulate with data using custom behavior. As an example, simple getters getAge() / getGrades() would be less error-prone and more readable than calls get("age") / get("grades").