Home > OS >  Collectors group by characteristic and minimum value of a field of said characteristic
Collectors group by characteristic and minimum value of a field of said characteristic

Time:07-17

Title is sort of confusing but I'm not sure how to explain my problem in a simple sentence.

I have a homework task to group an arraylist of rectangles by length (same as perimeter in this case) using streams and collectors and to calculate minimum width for each group. I have tried the following:

public static Map<Double, Double> groupIndenticalPerimeterWidth(ArrayList<Rectangle> rectangles){
        return rectangles.stream().collect(Collectors.groupingBy(Rectangle::getLength, Collectors.minBy((rectangle1, rectangle2) -> Double.compare(rectangle1.getWidth(), rectangle2.getWidth()))));
    }

which gets me a Map<Double, Optional<Rectangle>> and I can't figure out how to get the width of minimum rectangle in the second arguement of Collectors.groupingBy instead of Optional<Rectangle>

Any help appreciated

CodePudding user response:

group an arraylist of rectangles by length (same as perimeter in this case) using streams and collectors and to calculate minimum width for each group.

As you've noticed, collector minBy() produces Optional<Rectangle>.

To get double property from the optional result, you can use collector collectingAndThen(). It expects a collector (minBy() in this case) as the first argument, and a function that transforms the result produced by the collector as the second argument.

public static Map<Double, Double> groupIndenticalPerimeterWidth(ArrayList<Rectangle> rectangles){
    return rectangles.stream()
        .collect(Collectors.groupingBy(
            Rectangle::getPerimeter,
            Collectors.collectingAndThen(
                Collectors.minBy(Comparator.comparingDouble(Rectangle::getWidth)),
                result -> result.map(Rectangle::getWidth).orElseThrow() // `map` transforms Optional<Rectangle> into Optional<Double> and `orElseThrow` extracts the value from the optional
            )
        ));
}

Dummy Rectangle class:

public static class Rectangle {
    private double height;
    private double width;
    
    public double getPerimeter() {
        return 2 * (height   width);
    }

    // getters
}

Sidenote: it's highly advisable to use Java 8 static methods when you need to define a compactor.

CodePudding user response:

Instead of groupingBy with the necessity to extract an Optional you can do it easier with toMap with a merge function:

 public static Map<Double, Double> groupIndenticalPerimeterWidth(List<Rectangle> rectangles) {
    return rectangles.stream()
                     .collect(Collectors.toMap(
                           Rectangle::getPerimeter, Rectangle::getWidth, Math::min));
}
  • Related