Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update objects in one list based on values from second one using streams

I have two corresponding lists:

public class BookOverallData {
    private Long idOfBook;
    private String title;
    private String authour;
    private BigDecimal basePrice;
    private Integer discountRate;
}

public class TimeDiscount {    
    private Long idOfBook;
    private Integer discountRate;    
}

Set<BookOverallData> booksToReturn
Set<TimeDiscount> actualPromotions

The goal is to sum discounts, which means adding discountRate from actualPromotions to discountRate value from the booksToReturn list. The objects from both lists can be matched by idOfBook.

This is how I solved it

booksToReturn.forEach(
            p -> {
                final Optional<TimeDiscount> promotion = actualPromotions.stream().filter(ap -> Objects.equals(ap.getIdOfBook(), p.getIdOfBook())).findFirst();
                promotion.ifPresent(ap -> p.setDiscountRate(ap.getDiscountRate() + p.getDiscountRate()));
            }
        );

I'm just exploring streams and I think my solution is clumpy. How would you solve this problem in more elegant fashion, with usage of streams and functional approach?

like image 333
MyWay Avatar asked Nov 28 '18 15:11

MyWay


2 Answers

I'd first create a mapping from TimeDiscount::getIdOfBook to TimeDiscount:

Map<Long, TimeDiscount> accumulator = 
      actualPromotions.stream()
                     .collect(toMap(TimeDiscount::getIdOfBook, Function.identity()));

Then I'd do:

booksToReturn.forEach(e -> {
       TimeDiscount timeDiscount = accumulator.get(e.getIdOfBook());
       if (timeDiscount != null) e.setDiscountRate(e.getDiscountRate() + timeDiscount.getDiscountRate());
});

or if you want to stay with the use of Optional for some reason.

booksToReturn.forEach(e -> 
       Optional.ofNullable(accumulator.get(e.getIdOfBook()))
          .ifPresent(p -> e.setDiscountRate(e.getDiscountRate() + p.getDiscountRate()))
);

This improves upon the inefficient lookup in actualPromotions.stream() for each element of booksToReturn.

like image 72
Ousmane D. Avatar answered Sep 28 '22 06:09

Ousmane D.


One way you can do it is using:

booksToReturn.forEach(p -> actualPromotions.stream()
                .filter(actualPromotion -> actualPromotion.getIdOfBook().equals(p.getIdOfBook()))
                .forEach(actualPromotion -> p.setDiscountRate(p.getDiscountRate() + actualPromotion.getDiscountRate())));

Assuming actualPromotion.getIdOfBook() and p.getIdOfBook() would be unique across your Sets.

like image 27
Naman Avatar answered Sep 28 '22 06:09

Naman