Search code examples
javalambdacollectionsjava-streamfunctional-interface

Convert a List<Object> to a Map<String, List<Object>> and distinct the duplicates according to some properties of List's element?


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.


Solution

  • 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: