-1

For example, for two classes like this:

Class RawItem {
    private String category;
    private int code;
    private String name;
}

Class Item {
    private int code;
    private String name;
}

And list of RawItem as following:

[ { "category":"a", "code":1, "name":"item1" }, { "category":"a", "code":1, "name":"item1" }, { "category":"a", "code":2, "name":"item2" }, { "category":"b", "code":1, "name":"item1" }, { "category":"b", "code":1, "name":"item1" } ]

Turn it to a Map<String, List<Item>> like this:

{
   "a":[
      {
         "code":1,
         "name":"item1"
      },
      {
         "code":2,
         "name":"item2"
      }
   ],
   "b":[
      {
         "code":1,
         "name":"item1"
      }
   ]
}

Any reply will be greatly appreciated. thanks.

Wuaner
  • 929
  • 2
  • 14
  • 31

1 Answers1

3

The following groupingBy should work. As part of your additional requirements, we know that the equals method of RawItem objects cannot be used, which is why we are using a filter instead of the Stream distinct method (*):

    Set<Object> seen = ConcurrentHashMap.newKeySet();

    Map<String, List<Item>> m = 
       l.stream()
        .filter(item -> seen.add(item.category + ":" + item.name + ":" + item.code))
        .collect(Collectors.groupingBy(item -> item.category,
            Collectors.mapping(item -> new Item(item.code, item.name), 
                               Collectors.toList())));

Note that in order to allow the method distinct to work and remove duplicates, you need to have a proper implementation of the hashCode and equals methods in the RawItem class.

As an example, the following test:

List<RawItem> list = Arrays.asList(new RawItem("a", 1, "dfg"),
                                   new RawItem("a", 1, "dfg"),
                                   new RawItem("a", 1, "fdgdfdfgdg"),
                                   new RawItem("b", 1, "dfg"));

Map<String, List<Item>> map = // the above

System.out.println(map);

gives

{
   a=[Item{code=1, name='dfg'}, Item{code=1, name='fdgdfdfgdg'}],
   b=[Item{code=1, name='dfg'}]
}

(*) Edit:
As shared by the OP, the following posts help to design a solution based on a custom equivalence relationship with the stream API, when distinct() cannot be used:

Alexandre Dupriez
  • 3,026
  • 20
  • 25
  • This does not fully solve the problem since it does not get rid of items with identical content (duplicates) – Claudiu Guja Mar 19 '18 at 14:35
  • @ClaudiuGuja Indeed, fixed the post. – Alexandre Dupriez Mar 19 '18 at 14:42
  • Thanks for prompt response, I will try it tmrw because I already off my work after I asked this question, I’m using my phone to commenting now...but since distinct() need a proper implementation of equals, I doubt if it works since I can’t use category, code, name to uniquely identify a RawItem, it have other key fields that not listed here. So any other solution as a supplementary? – Wuaner Mar 19 '18 at 15:44
  • @Wuaner: Sure, I updated the post to take into account this additional requirement – Alexandre Dupriez Mar 19 '18 at 16:47
  • @AlexandreDupriez So brilliant~ can’t test it now since no computer around, but I know it will work! Accepted and thanks again. – Wuaner Mar 19 '18 at 20:05
  • It works~ saved my day, and I extract the Set _seen_ as a Predicate as [1](https://stackoverflow.com/a/27872852/1635855) [2](https://stackoverflow.com/a/27872086/1635855) [3](https://stackoverflow.com/a/42819145/1635855) illustrated. – Wuaner Mar 20 '18 at 02:49
  • @Wuaner Thank you for the feedback, I added these references to the post. I can also update it to replace the set *seen* with a predicate as you mention? – Alexandre Dupriez Mar 20 '18 at 10:40
  • @AlexandreDupriez I already done my work with predicate, but it would be great if we post it here for some others, I think?. Many thanks for this solution, btw. – Wuaner Mar 21 '18 at 03:11