2

I have a nested HashMap<String,Object> and I want to create a HashMap<String,String> by flattening the Hashmap. I have tried the solution from Recursively Flatten values of nested maps in Java 8. But I am unable to use the class FlatMap as mentioned in the answer.

I have also tried the solution in the question itself, still I am missing something. Then I found a similar use case and came up with the following solution. But it seems like I am missing something as a parameter for the lambda function flatMap .

public static void main(String[] args) {
  Map<String,Object> stringObjectMap= new HashMap<String,Object>();
  stringObjectMap.put("key1","value1");
  stringObjectMap.put("key2","value2");
  Map<String,Object> innerStringObjectMap = new HashMap<>();
  innerStringObjectMap.put("i1key3","value3");
  innerStringObjectMap.put("i1key4","value4");
  innerStringObjectMap.put("i1key5","value5");
  stringObjectMap.put("map1",innerStringObjectMap);
  Map<String,Object> innerStringObjectMap2 = new HashMap<>();
  innerStringObjectMap.put("i2key6","value6");
  innerStringObjectMap2.put("i2key7","value7");
  innerStringObjectMap.put("i1map2",innerStringObjectMap2);

  Map<String,Object> collect =
        stringObjectMap.entrySet().stream()
                .map(x -> x.getValue())
                .flatMap(x -> x) //I aint sure what should be give here
                .distinct(); //there was a collect as List which i removed.

  //collect.forEach(x -> System.out.println(x));

}

What is a better solution for flattening a nested map? I am not just interested in the values, but also the keys in the map. That is the reason why I decided to flatten the map to get another map (I am not sure if this is even possible)

EDIT - Expected Output

key1 - value1
key2-value2
map1 =""  //this is something i will get later for my purpose
i1key3=value3
.
.
i1map2=""
.
.
i2key7=value7
Vini
  • 1,978
  • 8
  • 40
  • 82
  • You have a mapping to both String and Map, which is why you have to declare that the root level map is a mapping from String to Object. In the entry set, you'll only get back `Object`, you don't know if you're dealing with a String or a nested Map without typechecking and casting. You should reconsider your design of this data structure. – nickb Jan 31 '18 at 16:15
  • @nickb: since I have no much clue about working with streams and Lambda functions, all i could do is adapt solutions that i found on the internet. But I will change the data structure and check what I could do. – Vini Jan 31 '18 at 16:32
  • What keys are supposed to be in the result map? With nested maps, each value is associated with two keys, further, inner map’s keys are not required to be unique across all maps… – Holger Feb 01 '18 at 12:33
  • I want the values matched with the corresponding inner key. I wil update the question to get an overview of the result that I am expecting. – Vini Feb 01 '18 at 12:36

2 Answers2

2

I modified the class from the mentioned answer according to your needs:

public class FlatMap {

  public static Stream<Map.Entry<?, ?>> flatten(Map.Entry<?, ?> e) {
    if (e.getValue() instanceof Map<?, ?>) {
      return Stream.concat(Stream.of(new AbstractMap.SimpleEntry<>(e.getKey(), "")),
                           ((Map<?, ?>) e.getValue()).entrySet().stream().flatMap(FlatMap::flatten));
    }

    return Stream.of(e);
  }
}

Usage:

Map<?, ?> collect = stringObjectMap.entrySet()
                                   .stream()
                                   .flatMap(FlatMap::flatten)
                                   .collect(Collectors.toMap(
                                       Map.Entry::getKey,
                                       Map.Entry::getValue,
                                       (u, v) -> throw new IllegalStateException(String.format("Duplicate key %s", u)),
                                       LinkedHashMap::new));

Attention:
Be sure to use the provided collect with a LinkedHashMap, otherwise the order will be screwed up.

JDC
  • 4,247
  • 5
  • 31
  • 74
  • i was not able to use it exactly like that. But I used the flatten function. *Usage* `Map collect = new HashMap<>(); stringObjectMap.entrySet() .stream() .flatMap(FlatMap::flatten).forEach(it -> { collect.put(it.getKey(), it.getValue()); });` – Vini Feb 02 '18 at 15:15
  • I had probelms near the `(u,v)`. – Vini Feb 02 '18 at 15:16
0

I have used the function from https://stackoverflow.com/a/48578105/5243291. But I used the function in a different way.

Map<Object, Object> collect = new HashMap<>();
stringObjectMap.entrySet()
        .stream()
        .flatMap(FlatMap::flatten).forEach(it -> {
          collect.put(it.getKey(), it.getValue());
});

Function again

public static Stream<Map.Entry<?, ?>> flatten(Map.Entry<?, ?> e) {
if (e.getValue() instanceof Map<?, ?>) {
  return Stream.concat(Stream.of(new AbstractMap.SimpleEntry<>(e.getKey(), "")),
          ((Map<?, ?>) e.getValue()).entrySet().stream().flatMap(FlatMap::flatten));
}

return Stream.of(e);
}
Vini
  • 1,978
  • 8
  • 40
  • 82