2

I have some data select from mysql. Then I want to format it. The data just like:

var json = [
  {
    "date": "2016-01",
    "number": "1",
    "code": "420000",
    "type": "false"
  },
  {
    "date": "2016-01",
    "number": "2",
    "code": "440000",
    "type": "false"
  },
  {
    "date": "2016-02",
    "number": "3",
    "code": "420000",
    "type": "false"
  },
  {
    "date": "2016-03",
    "number": "4",
    "code": "420000",
    "type": "true"
  },
  {
    "date": "2016-03",
    "number": "5",
    "code": "410000",
    "type": "false"
  },
  {
    "date": "2016-03",
    "number": "6",
    "code": "440000",
    "type": "true"
  },
  {
    "date": "2016-04",
    "number": "7",
    "code": "420000",
    "type": "false"
  },
  {
    "date": "2016-04",
    "number": "8",
    "code": "440000",
    "type": "false"
  }
];
console.log(json);

I want to groupingBy date, and partitioningBy type, and reducing to a Map. after format data just like

var json = {
  "2016-01": {
    "false": {
      "420000": "1",
      "440000": "2"
    }
  },
  "2016-02": {
    "false": {
      "420000": "3"
    }
  },
  "2016-03": {
    "false": {
      "410000": "5"
    },
    "true": {
      "420000": "4",
      "440000": "6"
    }
  },
  "2016-04": {
    "false": {
      "420000": "7",
      "440000": "8"
    }
  }
};
console.log(json);

I coding java8 Stream

public static void main(String[] args) {
    List<Map<String, String>> postmans = new ArrayList<>();

    // "420000" : post area code, "false": just paper(without goods)
    postmans.add(createMap("2016-01", "1", "420000", "false"));
    postmans.add(createMap("2016-01", "2", "440000", "false"));
    postmans.add(createMap("2016-02", "3", "420000", "false"));
    postmans.add(createMap("2016-03", "4", "420000", "true"));
    postmans.add(createMap("2016-03", "5", "410000", "false"));
    postmans.add(createMap("2016-03", "6", "440000", "true"));
    postmans.add(createMap("2016-04", "7", "420000", "false"));
    postmans.add(createMap("2016-04", "8", "440000", "false"));

    // before, I use fastjson Library
    System.out.println(JSONObject.toJSONString(postmans));


    Map<String, Map<Boolean, Map>> data = postmans.stream()
            .collect(Collectors.groupingBy(d -> d.get("date"), TreeMap::new,
                    Collectors.partitioningBy(d-> d.get("type").equals("true"),
                            Collectors.reducing(new HashMap(), (d1, d2)->{
                                Object code = d2.get("code");
                                Object number = d2.get("number");
                                // I think bug in reducing
                                d1.put(code, number);
                                return d1;
                            }))));
    // after, I use fastjson Library
    System.out.println(JSONObject.toJSONString(data));

}

private static Map<String,String> createMap(String date, String number, String code, String type){
    Map<String, String> map = new HashMap<>();
    map.put("date", date);
    map.put("number", number);
    map.put("code", code);
    map.put("type", type);
    return map;
}

but, real result is

var json = {
  "2016-01": {
    "false": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    },
    "true": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    }
  },
  "2016-02": {
    "false": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    },
    "true": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    }
  },
  "2016-03": {
    "false": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    },
    "true": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    }
  },
  "2016-04": {
    "false": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    },
    "true": {
      "410000": "5",
      "420000": "7",
      "440000": "8"
    }
  }
};
console.log(json);

I think the cause of this bug in reduing. When the partitioningBy get a new List <Map>, the old d1 still exists.

I just want to partitioningBy result, each List <Map> compressed to a Map, I can guarantee that its key (code) is unique.

I am sorry for The code is so long and My English is poor. Thank you for help me!

shmosel
  • 49,289
  • 6
  • 73
  • 138
Ahaochan
  • 47
  • 7
  • 1
    Your reducing unit is always the exact same `HashMap` instance. You should have different maps for reduce operation on every branch. – M. Prokhorov Sep 10 '17 at 17:50

1 Answers1

1

There are two main mistakes here. Collectors.reducing, since this is a reduction, needs to return a new instance all the time and basically the second is that your logic is flawed:

Map<String, Map<Boolean, Map<String, String>>> data = postmans.stream()
            .collect(Collectors.groupingBy(
                    d -> d.get("date"),
                    TreeMap::new,
                    Collectors.partitioningBy(
                            d -> d.get("type").equals("true"),
                            Collectors.reducing(
                                    new HashMap<>(),
                                    (left, right) -> {

                                        Map<String, String> map = new HashMap<>();

                                        String leftCode = left.get("code");
                                        String leftNumber = left.get("number");

                                        if (leftCode == null) {
                                            map.putAll(left);
                                        } else {
                                            map.put(leftCode, leftNumber);
                                        }

                                        String rightCode = right.get("code");
                                        String rightNumber = right.get("number");

                                        map.put(rightCode, rightNumber);

                                        return map;

                                    }))));

You also have a problem of storing Date as String and doing TreeMap::new - which basically sorts Stringss, which is probably not the sorting you want. In general this entire approach of having everything as a String makes the code hardly readable.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Can you add the execution process? Why do you want new `HashMap` and `putAll (left)`? – Ahaochan Sep 11 '17 at 08:30
  • 1
    @Ahaochan the execution process is quite easy, you could find it for yourself if you print `left` and `right` maps before they are merged and after (right before `return map`). The second part is basically the diff between `reduce` and `collect` - reduce needs to return a new instance all the time, you can read more here : https://stackoverflow.com/questions/22577197/java-8-streams-collect-vs-reduce – Eugene Sep 11 '17 at 08:53
  • Thank! I had a simple mistake of Java values and references. The `identity` is created only once and is restored every time `List`.The error in my code is to modify `identity`, which results in the failure of identity recovery. – Ahaochan Sep 11 '17 at 09:30