Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java8 stream style for retrieving a inner part of map through a field list?

For example, given a map like below:

{
  "k1": {
    "k2": {
      "k3": {
        "k4": "v"
      }
    }
  }
}

and a field list ["k1","k2","k3"], I need to retrieve the part {"k4": "v"}.

Below is my java7-style code:

// Ignore the map building code.
Map map1 = new HashMap();
Map map2 = new HashMap();
Map map3 = new HashMap();
Map map4 = new HashMap();
map4.put("k4", "v");
map3.put("k3", map4);
map2.put("k2", map3);
map1.put("k1", map2);
Map map = map1;
System.out.println(map); //=> {k1={k2={k3={k4=v}}}}

// Code to be transformed to java8 style
List<String> fields = Arrays.asList("k1", "k2", "k3");
for(String field: fields) {
    map = (Map) map.get(field);
}
System.out.println(map); //=> {k4=v}

Then how to transform above code to java 8 stream style?

like image 462
Run Avatar asked Feb 06 '23 02:02

Run


1 Answers

I don’t think that there is any benefit in converting this into a functional style; the loop is fine and precisely expresses what you are doing.

But for completeness, you can do it the following way:

map = (Map)fields.stream()
    .<Function>map(key -> m -> ((Map)m).get(key))
    .reduce(Function.identity(), Function::andThen).apply(map);

This converts each key to a function capable of doing a map lookup of that key, then composes them to a single function that is applied to you map. Postponing the operation to that point is necessary as functions are not allowed to modify local variables.

It’s also possible to fuse the map operation with the reduce operation, which allows to omit the explicit type (<Function>):

map = (Map)fields.parallelStream()
 .reduce(Function.identity(), (f, key)->f.andThen(m->((Map)m).get(key)), Function::andThen)
 .apply(map);

Maybe you recognize now, that this is a task for which a simple for loop is better suited.

like image 76
Holger Avatar answered Feb 09 '23 00:02

Holger