Search code examples
javajava-stream

How to group success and failure items in a list using Java Streams


Consider a scenario where there is an object which contains the below member variables:

class Result{
    private String message;
    private boolean success;
}

After a query, we get a result set List.

Consider a scenario where there are 5 result set (instances of Result class): 3 out of the 5 instances have the success value as FALSE and TRUE for the rest 4. For example:

Row 1: success = false | message: failed for reason 1.

Row 2: success = true | message: success.

Row 3: success = false | message: failed for reason 2.

Row 4: success = false | message: failed for reason 3.

Row 5: success = true | message: success.

We need to group the result set in this particular way: Display the success = false rows at the top (in the same order that they were retrieved) and one grouped success message for all the rest rows.

So the output should be:

Row 1: success = false | message: failed for reason 1.

Row 2: success = false | message: failed for reason 2.

Row 3: success = false | message: failed for reason 3.

Row 4: success = true | message: success for the rest. (no need to display the same number of Rows for the success)

I tried for grouping the list based on success:

List<Result> resultSet;
Map<Boolean, List<Result>> result = resultset.stream().collect(Collectors.groupingBy(Result::success,Collectors.toList()));

But how to extract the message and also re-arrange the order by making Failed rows in the top and success row at the last


Solution

  • If you want a map you can use Collectors.partitioningBy which partitions using a boolean key.

    Map<Boolean, List<Result>> result = resultSet.stream()
                .collect(Collectors.partitioningBy(Result::getSuccess));
    

    The above presumes you have a getter (here getSuccess) defined to return the success boolean.

    Updated

    Generate some test data.

    record Result(String getMessage, boolean getSuccess) {
    }
    Random r = new Random();
    Function<Integer, Result> createResult = rpw -> {
        boolean b = r.nextBoolean();
        return new Result((b
                ? "Row %2d: success = true  | message: success.".formatted(row)
                : "Row %2d: success = false | message: failed for reason %2d."
                        .formatted(row, r.nextInt(30) + 1)),
                b);
    };
    List<Result> resultSet = IntStream.range(1, 15)
            .mapToObj(createResult::apply).toList();
    

    Now stream the test data and create a map of the results.

    Map<Boolean, List<Result>> result = resultSet.stream()
            .collect(Collectors.partitioningBy(Result::getSuccess));
    

    Now print the map, first placing unsuccessful results on the stream and then placing a single success on the stream.

    Stream.of(false, true).flatMap(b -> result.get(b).stream()
            .map(Result::getMessage).limit(b ? 1 : Long.MAX_VALUE))
            .forEach(System.out::println);
    System.out.println("All other messages indicate success.");
    

    prints something like this.

    Row  2: success = false | message: failed for reason 15.
    Row  3: success = false | message: failed for reason  5.
    Row  5: success = false | message: failed for reason 12.
    Row  7: success = false | message: failed for reason 25.
    Row  9: success = false | message: failed for reason 10.
    Row 10: success = false | message: failed for reason 23.
    Row 14: success = false | message: failed for reason 24.
    Row  1: success = true  | message: success.
    All other messages indicate success.
    

    Or you can just print the unsuccessful messages.

    result.get(false).forEach(res->System.out.println(res.getMessage));
    System.out.println("All other messages indicate success.");
    

    prints

    Row  2: success = false | message: failed for reason 15.
    Row  3: success = false | message: failed for reason  5.
    Row  5: success = false | message: failed for reason 12.
    Row  7: success = false | message: failed for reason 25.
    Row  9: success = false | message: failed for reason 10.
    Row 10: success = false | message: failed for reason 23.
    Row 14: success = false | message: failed for reason 24.
    All other messages indicate success.