stephCurry
stephCurry

Reputation: 101

convert a list to a nested list with parent-children relation in Java 8 by stream

The Arraylist is like below:

[{
  "id": 1,
  "parent_id": 0,
  "name": "Top1",
}, {
  "id": 2,
  "parent_id": 1,
  "name": "Second Layer1",
},{
  "id": 3,
  "parent_id": 0,
  "name": "Top2",
},{
  "id": 4,
  "parent_id": 1,
  "name": "Second Layer2",
}]

The output should be like:

[{
  "id": 1,
  "parent_id": 0,
  "name": "Top1",
  "children": [{
    "id": 2,
    "parent_id": 1,
    "name": "Second Layer1",
  }, {
    "id": 4,
    "parent_id": 1,
    "name": "Second Layer2",
  }]
}, {
  "id": 3,
  "parent_id": 0,
  "name": "Top2",
}]

My current solution is that distinguishing "top layers" and "second layers", then converting the "second layers" to a hashmap group by "parent_id", finally iterating the "top layers" and match up the id of "top layer" with the key of that hashmap.
The implementation is like blew:

List<Org> topLayers = list.stream().filter(org -> org.getParent_id==0).collect(Collectors.toList());

List<Org> secondLayers = list.stream().filter(org -> org.getParent_id!=0).collect(Collectors.toList());

Map<Long, List<Org>> hashMap = secondLayers.stream().collect(Collectors.groupingBy(Org::parent_id));

topLayers.stream().forEach(topOrg -> {
    topOrg.setChildren(hashMap.get(topOrg.getId()));
});

As you could see, it costs 4 steps to figure out.
Is there a more effective way to solve it?

Extension: What if is there more than 2 layers? like recursive

Upvotes: 2

Views: 2782

Answers (4)

AnnKont
AnnKont

Reputation: 506

You also can do like this

Map<Long, Org> hashMap = list.stream()
                .filter(org -> org.getParent_id!=0)
                .collect(Collectors.groupingBy(Org::parent_id));
List<Org> listWithChilds = list.stream()
                .filter(org -> org.getParent_id==0)
                .map(topOrg -> {
                    topOrg.addChildren(hashMap.get(topOrg.getId()));
                })
                .collect(Collectors.toList());

Upvotes: 4

Manikant Gautam
Manikant Gautam

Reputation: 3591

Here is the online demo . you can do this by using forEach and then useing filter

originalList.forEach(el->{
        int parentId = Integer.parseInt(el.get("parent_id").toString());
        if(parentId>0) {
            List<Map<String, Object>> childList = originalList.stream().filter(e->Integer.parseInt(el.get("id").toString())==parentId).collect(Collectors.toList());
            if(!childList.isEmpty()) {
                el.put("children",childList );
            }
        }
    });

Upvotes: 1

Neo
Neo

Reputation: 51

You already said it, recursive.

convert(List src, List target) {
    for each item with parent id as (target is empty ? 0 : target's id):
        add it to (target is empty ? target : target's children)
        convert(src, item)
}

Upvotes: -1

Sumit Singh
Sumit Singh

Reputation: 15896

You can simply group all your data using group by based on parentID then assign each parent to that list:

Map<Long, List<Org>> result = list.stream()
  .collect(Collectors.groupingBy(Org::parent_id));

then:

list.stream().filter(org -> org.getParent_id==0).forEach(v -> {
    v.addChildren(result.get(v.getId()));
});

There can we many way to do this. You can try others too.

Upvotes: 2

Related Questions