1
\$\begingroup\$

I need to round BigDecimal numbers according to the Largest Remainder Method (LRM). Please reference this SO post with this answer. Here's my version of the algorithm in Java:

public class LargestRemainderMethod {

    public static List<BigDecimal> roundValues(List<BigDecimal> values, int roundTo, BigDecimal desiredSum) {
        // 1. Sort by largest remainder
        values.sort((d1, d2) ->
            -d1.remainder(BigDecimal.ONE.divide(BigDecimal.TEN.pow(roundTo))).
            compareTo(d2.remainder(BigDecimal.ONE.divide(BigDecimal.TEN.pow(roundTo)))));

        // 2. Rounding everything down
        List<BigDecimal> roundedValues = new ArrayList<BigDecimal>(values.size());
        for (BigDecimal value : values) {
            roundedValues.add(value.setScale(roundTo, RoundingMode.DOWN));
        }

        // 3. Now round up values until desired sum is achieved or we have
        //    no other values to round up
        for (int i = 0; i < values.size(); i++) {

            BigDecimal roundedSum = roundedValues.stream().reduce(BigDecimal.ZERO, BigDecimal::add);

            if (roundedSum.compareTo(desiredSum) != 0) {
                roundedValues.set(i, values.get(i).setScale(roundTo, RoundingMode.UP));
            }
        }
        return roundedValues;
    }
}

I apply LRM to a list of objects with a BigDecimal value. For example:

List<BigDecimal> fractionsOfOwnership = 
    owners.stream()
          .map(Owner::getFractionOfOwnership)
          .collect(Collectors.toList());

List<BigDecimal> roundedValues = LargestRemainderMethod.roundValues(fractionsOfOwnership, 1, new BigDecimal("1.0"));

Next, for each owner, I need overwrite the original fractionOfOwnership with the corresponding rounded value determined by the LRM roundedValues method.

Currently, I sort the owners as I know they'll be sorted in LargestRemainderMethod.roundValues. Then, I iterate through the owners list setting the rounded values using the same index.

owners.sort((o1, o2) ->
        -o1.getFractionOfOwnership().remainder(BigDecimal.ONE.divide(BigDecimal.TEN.pow(roundTo))).
        compareTo(o1.getFractionOfOwnership().remainder(BigDecimal.ONE.divide(BigDecimal.TEN.pow(roundTo)))));
for (int i = 0; i < owners.length; i++) {
     owners.get(i).setFractionOfOwnership(roundedValues.get(i));
 }

But this violates DRY. How can this be improved such that I'm not duplicating code?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.