Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: How to properly manipulate BigDecimal array using functional programming?

So, how to get outcome of this code using functional programming:

public static final List<BigDecimal> numbers = Arrays.asList(
    new BigDecimal("15"), new BigDecimal("10"), new BigDecimal("17"),
    new BigDecimal("30"), new BigDecimal("18"), new BigDecimal("23"),
    new BigDecimal("5"), new BigDecimal("12") );

BigDecimal totalOfReducedNumbers = BigDecimal.ZERO;
   for(BigDecimal number : numbers) {
       if(number.compareTo(BigDecimal.valueOf(20)) > 0) 
           totalOfReducedNumbers = 
         totalOfReducedNumbers.add(number.multiply(BigDecimal.valueOf(0.9)));
   }
System.out.println("Total of reduced numbers: " + totalOfReducedNumbers);

Which throws out "Total of reduced numbers: 47.7"

How to get the same outcome using functional programming tools like map(), reduce() etc?

like image 614
juststuck Avatar asked Dec 05 '18 18:12

juststuck


2 Answers

By performing the same operations, first filter values (you only want values greater than 20). Then multiply those values by 0.9 and finally reduce the terms by performing addition. Like,

BigDecimal TWENTY = BigDecimal.valueOf(20);
BigDecimal POINT9 = BigDecimal.valueOf(0.9);
System.out.println("Total of reduced numbers: " + numbers.stream()
        .filter(x -> x.compareTo(TWENTY) > 0)
        .map(x -> x.multiply(POINT9)).reduce((a, b) -> a.add(b)).get());

Outputs (as requested)

Total of reduced numbers: 47.7

And, as suggested in the comments, we can further improve that with a method reference and using orElse would be safer than a raw get(). Like,

System.out.println("Total of reduced numbers: " + numbers.stream() 
        .filter(x -> x.compareTo(TWENTY) > 0) 
        .map(x -> x.multiply(POINT9)) 
        .reduce(BigDecimal::add) 
        .orElse(BigDecimal.ZERO));
like image 135
Elliott Frisch Avatar answered Sep 19 '22 23:09

Elliott Frisch


The for loop implementation is a typical "reduce" pattern (also known as "fold", "Aggregate" pattern in some other languages)

You're looking for filter --> reduce method:

BigDecimal reduce = 
         numbers.stream()
                .filter(n -> n.compareTo(BigDecimal.valueOf(20)) > 0)
                .reduce(BigDecimal.ZERO, 
                       (a, b) -> a.add(b.multiply(BigDecimal.valueOf(0.9))));

You can further minimise the number of objects contructed by caching BigDecimal.valueOf(20) and BigDecimal.valueOf(0.9) as shown in @Elliot's answer i.e.:

BigDecimal TWENTY = BigDecimal.valueOf(20);
BigDecimal POINT9 = BigDecimal.valueOf(0.9);
BigDecimal reduce = 
             numbers.stream()
                    .filter(n -> n.compareTo(TWENTY) > 0)
                    .reduce(BigDecimal.ZERO, 
                           (a, b) -> a.add(b.multiply(POINT9)));
like image 30
Ousmane D. Avatar answered Sep 20 '22 23:09

Ousmane D.