Adhyatmik
Adhyatmik

Reputation: 1118

How to use partitioningBy and then sort resulting lists separately using Java Streams

I have an object like following:

public class Resource {
  int level;
  String identifier;
  boolean isEducational;

  public Resource(String level, String identifier, boolean isEducational) {
            this.level = level;
            this.identifier = identifier;
            this.isEducational = isEducational;
        }

 // getter and setters 
}

And a list of these Resources like:

List<Resource> resources = Arrays.asList(new Resource(4, "a", true ),
                                                new Resource(4, "b", false),
                                                new Resource(3, "c", true ),
                                                new Resource(3, "d", false ),
                                                new Resource(2, "e", true ),
                                                new Resource(2, "f" , false));

I want to sort this list by their level property but this sorting should be done separately for isEducational resources and non-isEducational resources.

So, after sorting, the resultant list should be in the following order:

[Resource e, Resource c, Resource a, Resource f, Resource d, Resource b]

// basically, isEducational sorted first, followed by non-educational resources

So I tried following:

List<Resource> resources1 = resources.stream()
                .collect(partitioningBy(r -> r.isEducational()))
                .values()
                .stream()
                .map(list -> {
                    return list
                            .stream()
                            .sorted(comparing(r -> r.getLevel()))
                            .collect(toList());
                })
                .flatMap(Collection::stream)
                .collect(toList());


resources1.stream().forEach(System.out::println);

And it prints the output as:

Resource{level='2', identifier='f', isEducational='false'}
Resource{level='3', identifier='d', isEducational='false'}
Resource{level='4', identifier='b', isEducational='false'}
Resource{level='2', identifier='e', isEducational='true'}
Resource{level='3', identifier='c', isEducational='true'}
Resource{level='4', identifier='a', isEducational='true'}

Which is opposite of what I want i.e. its printing non-educational first, followed by educational resources

Is there a better way to achieve this ? I do not want to iterate the list again to rearrange it. Thanks.

Upvotes: 2

Views: 246

Answers (1)

Eritrean
Eritrean

Reputation: 16498

No need to using partitioningBy at all. You just need two comparators to first to compare by isEducational and then by level, which you can chain by using Comparator.thenComparing

resources.stream()
         .sorted(Comparator.comparing(Resource::isEducational).reversed().thenComparing(Resource::getLevel))
         .forEach(System.out::println);

You can introduce variables for the comparators to make your code more readable or if you want to reuse them in a flexible manner:

Comparator<Resource> byIsEdu = Comparator.comparing(Resource::isEducational).reversed();
Comparator<Resource> byLevel = Comparator.comparing(Resource::getLevel);

resources.stream()
         .sorted(byIsEdu.thenComparing(byLevel))
         .forEach(System.out::println);

Upvotes: 6

Related Questions