Filtering list of object inside list of objects

Viewed 52

So i have a list of objects:

        List<GpbCodesAndVersions> lst = Arrays.asList(new GpbCodesAndVersions("code1", 1, Arrays.asList(new General(1), new General(2))),
            new GpbCodesAndVersions("code2", 2,  Arrays.asList(new General(1), new General(5))),
            new GpbCodesAndVersions("code3", 3,  Arrays.asList(new General(2), new General(3))),
            new GpbCodesAndVersions("code4", 4,  Arrays.asList(new General(2), new General(4))));

And i want to filter this list and get list which includes only General(1) fields: GpbCodesAndVersions(code=code1, version=1, content=[General(status=1)]), GpbCodesAndVersions(code=code2, version=2, content=[General(status=1)])

My code is working, but maybe it possible to do it more beautiful ? My code:

    List<GpbCodesAndVersions> versionOneGeneral = lst.stream().peek(x -> {
        List<General> generals = x.getContent().stream().filter(y -> y.getStatus().equals(1)).toList();
        x.setContent(generals);
    }).filter(z -> !z.getContent().isEmpty()).toList();
2 Answers

Using peek is generally not recommended for such tasks, because it forces you to use side-effects to modify the list in-place.

It is better to use side-effect-free map to create a new version of the list (functional-style):

final List<GpbCodesAndVersions> list = Arrays.asList(
        new GpbCodesAndVersions("code1", 1, Arrays.asList(new General(1), new General(2))),
        new GpbCodesAndVersions("code2", 2,  Arrays.asList(new General(1), new General(5))),
        new GpbCodesAndVersions("code3", 3,  Arrays.asList(new General(2), new General(3))),
        new GpbCodesAndVersions("code4", 4,  Arrays.asList(new General(2), new General(4))));

final List<GpbCodesAndVersions> newList = list.stream()
    .map(x -> new GpbCodesAndVersions(
            x.getCode(),
            x.getId(),
            x.getContent().stream().filter(g -> g.getStatus() == 1).toList()))
    .filter(Predicate.not(x -> x.getContent().isEmpty()))
    .toList();

This will leave all GpbCodesAndVersions unmodified in the original list and creates a new list containing new GpbCodesAndVersions with a new list of generals. Both lists are now independent and modifications of one list will not affect the other list (which avoids unindented modifications).

I think anyMatch can help, thanks for correcting me @knittl

Just a small thing, it will immediately apply the map function after filter out one item from list, means the process will be in below order(in this case, first two item can be processed after filter), means it will iterate the list only once

filter, map, filter, map, filter, filter, other actions
List<GpbCodesAndVersions> result1 = list.stream()
        .filter(x -> x.getContent().stream().anyMatch(y -> y.getStatus() == 1))
        .map(x -> {
            List<General> generals2 = x.getContent().stream().filter(g -> g.getStatus() == 1).toList();
            x.setContent(generals2);
            return x;
        })
        .collect(Collectors.toList());


System.out.println("result1 = " + result1);

result1 = [GpbCodesAndVersions(code=code1, v=1, content=[General(status=1)]), GpbCodesAndVersions(code=code2, v=2, content=[General(status=1)])]

Related