3

I have the following method:

public String getAllDangerousProductsName(Offer offer){
    return offer.getOfferRows().stream()
           .filter(row -> row.isDangerousGood())
            .map(row -> row.getItemInformation().getOfferTexts().getName())
            .collect(Collectors.joining(","));
}

I want to reuse this method for row.isBulkyGood(). What I am doing currently is

public String getAllBulkyProductsName(Offer offer){
    return offer.getOfferRows().stream()
            .filter(row -> row.isBulkyGood())
            .map(row -> row.getItemInformation().getOfferTexts().getName())
            .collect(Collectors.joining(","));
}

... which is basically code repetition. Is there a way I can pass the function as method parameter to optimize this to have one method for both the filter condition?

Vinaya Nayak
  • 1,083
  • 3
  • 16
  • 29

3 Answers3

7

You can pass the Predicate used in the filter right to the method which is the only thing that differs in the methods.

Assuming offer.getOfferRows() returns List<OfferRow>, then:

public String getAllDangerousProductsName(Offer offer, Predicate<OfferRow> predicate) {
    return offer.getOfferRows().stream()
            .filter(predicate)
            .map(row -> row.getItemInformation().getOfferTexts().getName())
            .collect(Collectors.joining(","));
}

Usage becomes fairly simple:

// using lambda expression
String str1 = getAllDangerousProductsName(offer, row -> row.isDangerousGood());
String str2 = getAllDangerousProductsName(offer, row -> row.isBulkyGood());
// using method reference
String str1 = getAllDangerousProductsName(offer, OfferRow::isDangerousGood);
String str2 = getAllDangerousProductsName(offer, OfferRow::isBulkyGood);
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
2

Yes, you could add a Predicate<OfferRow> argument to this method and then use method references OfferRow::isDangerousGood or OfferRow::isBulkyGood:

public static String getAllProductsNameByPredicate(Offer offer, Predicate<OfferRow> predicate){
    return offer.getOfferRows().stream()
            .filter(predicate)
            .map(row -> row.getName())
            .collect(Collectors.joining(","));
}

// invoke with different predicates
getAllProductsNameByPredicate(myOffer, OfferRow::isDangerousGood);
getAllProductsNameByPredicate(myOffer, OfferRow::isBulkyGood);

If it is needed to negate the condition of the predicate, Predicate.not may be used since Java 11.

For Java 8-10, a utility method may be implemented using Predicate.negate():

public static <R> Predicate<R> not(Predicate<R> predicate) {
    return predicate.negate();
}

getAllProductsNameByPredicate(myOffer, not(OfferRow::isDangerousGood));

Nowhere Man
  • 19,170
  • 9
  • 17
  • 42
1

Yes, create a filterAndJoin:

public String filterAndJoin(Offer offer, Predicate<Row> predicate){
    return offer.getOfferRows().stream()
        .filter(predicate)
        .map(row -> row.getItemInformation().getOfferTexts().getName())
        .collect(Collectors.joining(","));
  }

and then change the calls to

public String getAllDangerousProductsName(Offer offer){
    return filterAndJoin(offer, Row::isBulkyGood);
}
luk2302
  • 55,258
  • 23
  • 97
  • 137