michelle
michelle

Reputation: 197

Iterate over nested (multi-level) hashmap

I am trying to write a java program to iterate over a multi-level hashmap. For example, I have a HashMap <String, Object>, where Object can be another HashMap<String, Object>.

The level of this hashmap can be n (>5).

Can someone give me a hint on how to write it in java? Does java provide some utility?

Thanks

Upvotes: 5

Views: 7638

Answers (6)

Chomeh
Chomeh

Reputation: 1066

// Depth first traversal where entry keys are represented as the list of keys to address the current element
static Stream<Map.Entry<List<String>, Object>> flatten(List<String> keys, Stream<Map.Entry<String, Object>> entries) {
    return entries.flatMap(entry -> {
        List<String> keysWithCurrentKey = Stream.concat(keys.stream(), Stream.of(entry.getKey())).collect(Collectors.toList());
        if(entry.getValue() instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) entry.getValue();
            return flatten(
                    keysWithCurrentKey,
                    map.entrySet().stream()
            );
        } else {
            return Stream.of(Map.entry(keysWithCurrentKey, entry.getValue()));
        }
    });
}

static Stream<Map.Entry<List<String>, Object>> flatten(Stream<Map.Entry<String, Object>> entries) {
    return flatten(List.of(), entries);
}

e.g

    Map<String, Object> map = Map.of(
            "a", "b",
            "c", "d",
            "e", Map.of("f", Map.of("g", "h"))
    );

    flatten(map.entrySet().stream())
            .forEach(flattenedEntry -> {
                System.out.println(flattenedEntry);
            });

produces

[e, f, g]=h
[a]=b
[c]=d

Upvotes: 0

Muhammad Maroof
Muhammad Maroof

Reputation: 303

When you have n level depth it is better to use recursion, I have faced the same problem where my map depth level is > 3. Below is the solution which work for me.

private void mapIterator(Map<String, Object> map) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        if (entry.getValue() instanceof Map) {
             mapIterator((Map<String, Object>) entry.getValue());
        } else {
            //your logic goes here.
            System.out.println(entry.getKey()+" : "+ entry.getValue());
        }
    }
}

Upvotes: 1

Tavo
Tavo

Reputation: 3151

I don't mean to steal @David Frank's thunder. On the opposite, I'm giving him all the credit. Here is a slight modification over his code where you would get a flat output map with a dot-separated keySet:

public void iterate(String currentKey, Map<String, Object> map, Map<String, String> out) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        if (entry.getValue() instanceof Map) {
            iterate(currentKey + "." + entry.getKey(), (Map<String, Object>) entry.getValue(), out);
        } else {
            out.put(currentKey + "." + entry.getKey(), entry.getValue().toString());
        }
    }
}

To keep it as simple as possible, I'm not removing the . at the beginning of each key.

Upvotes: 0

Brian
Brian

Reputation: 346

Without knowing anything about the nature of your program, here is a generic example of recursion with a hashmap:

public void printAll(HashMap<String, Object> map) {
  for (Object o : map.values()) {
    if (o instanceof HashMap) {
      printAll((HashMap<String, Object>) o);
    } else {
      System.out.println(o.toString());
    }
  }
}

May not be 100% correct syntax, just typing from memory.

Upvotes: 2

David Frank
David Frank

Reputation: 6092

public void iterate(Map<String, Object> map) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        System.out.println("Key is: " + entry.getKey());
        if (entry.getValue() instanceof Map) {
            System.out.println("Map found, digging further");
            iterate((Map<String, Object>) entry.getValue());
        } else {
            System.out.println("Leaf found, value is: " + entry.getValue());
        }
    }
}

This does a depth-first iteration. However, since java is a strongly typed language, nesting hashmaps and other types is generally not a good idea. Most of the time there are solutions which do not involve such hacking.

Upvotes: 5

Kyle Emmanuel
Kyle Emmanuel

Reputation: 2221

You can start by checking whether the type of object is a HashMap and then check its length whether it has more contents or not.

Upvotes: 0

Related Questions